找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1344|回复: 0

[分享] Accessing the AutoCAD objects referred to by fields using .NET

[复制链接]

已领礼包: 859个

财富等级: 财运亨通

发表于 2014-5-11 17:11:06 来自手机 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 csharp 于 2014-5-12 07:56 编辑

http://through-the-interface.typepad.com/through_the_interface/2007/07/accessing-the-a.html

Accessing the AutoCAD objects referred to by fields using .NET
Thanks to Wolfgang Ruthensteiner for suggesting this excellent topic a comment to this previous post. Here's Wonfgang's question:
How do I read back the field code with C# (from an attribute e.g.)?
I am linking room-label blocks with polylines, using fields inside an attribute to display the polyline's area property.
Later I want to find out programatically, which polyline a certain block is linked to by evaluating the field in the attribute (extracting the objectId).
This was actually quite tricky, and one I needed the help of our old friend, ArxDbg, to solve (see here for some information on this very useful ObjectARX sample). I should say up-front that there may well be a simpler way to access the information - the below technique is to some degree relying on the database structure (which might be considered an implementation detail). I may be missing a higher-level API providing a simpler way to access the information, but there you have it.
The full text of the field expression is stored in an AcDbField object (which is accesible through the Autodesk.AutoCAD.DatabaseServices.Field) which exists inside a field dictionary in the text object's (or attribute's) extension dictionary. So here's what needs to happen:
  • Select the MText object (I chose to use MText in the below code, as it was a bit more work to allow attribute selection within a block - left as an exercise for the reader :-)
  • Open the MText object's extension dictionary
  • Open the nested field dictionary
  • Access the field object stored therein
At this stage you have your text string with all the uninterpreted field codes. For those of you that are interested, I remember an important decision at the time we implemented fields in AutoCAD: that we should maintain the existing protocol and not return uninterpreted field codes from the standard text access properties/methods. This was largely to avoid migration issues for applications that depended on the data to be returned in its evaluated form. But it clearly means a bit more work if you want to get at the underlying codes.
So once we have our codes, we then want to get back to the "referred" object(s). I implemented a simple function that parses a string for the following sub-string:
%<\_ObjId XXX>%
... where XXX is a string representing the ObjectId. The code then uses a conversion function to get an integer from the string, and create an ObjectId from the integer. We return the ID to the calling function, where we can then open it and find out more about it.
So that's the description - here's the C# code implementing it:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;

namespace FieldExtraction
{
  public class Commands
  {
    [CommandMethod("GFL")]
    static public void GetFieldLink()
    {
      Document doc =
        Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      Editor ed = doc.Editor;

      // Ask the user to select an attribute or an mtext
      PromptEntityOptions opt =
        new PromptEntityOptions(
          "\nSelect an MText object containing field(s): "
        );
      opt.SetRejectMessage(
        "\nObject must be MText."
      );
      opt.AddAllowedClass(typeof(MText), false);
      PromptEntityResult res =
        ed.GetEntity(opt);

      if (res.Status == PromptStatus.OK)
      {
        Transaction tr =
          doc.TransactionManager.StartTransaction();
        using (tr)
        {
          // Check the entity is an MText object
          DBObject obj =
            tr.GetObject(
              res.ObjectId,
              OpenMode.ForRead
            );

          MText mt = obj as MText;
          if (mt != null)
          {
            if (!mt.HasFields)
            {
              ed.WriteMessage(
                "\nMText object does not contain fields."
              );
            }
            else
            {
              // Open the extension dictionary
              DBDictionary extDict =
                (DBDictionary)tr.GetObject(
                  mt.ExtensionDictionary,
                  OpenMode.ForRead
                );

              const string fldDictName = "ACAD_FIELD";
              const string fldEntryName = "TEXT";
              // Get the field dictionary
              if (extDict.Contains(fldDictName))
              {
                ObjectId fldDictId =
                  extDict.GetAt(fldDictName);
                if (fldDictId != ObjectId.Null)
                {
                  DBDictionary fldDict =
                    (DBDictionary)tr.GetObject(
                      fldDictId,
                      OpenMode.ForRead
                    );

                  // Get the field itself
                  if (fldDict.Contains(fldEntryName))
                  {
                    ObjectId fldId =
                      fldDict.GetAt(fldEntryName);
                    if (fldId != ObjectId.Null)
                    {
                      obj =
                        tr.GetObject(
                          fldId,
                          OpenMode.ForRead
                        );
                      Field fld = obj as Field;
                      if (fld != null)
                      {
                        // And finally get the string
                        // including the field codes
                        string fldCode = fld.GetFieldCode();
                        ed.WriteMessage(
                          "\nField code: "
                          + fldCode
                        );

                        // Loop, using our helper function
                        // to find the object references
                        do
                        {
                          ObjectId objId;
                          fldCode =
                            FindObjectId(
                              fldCode,
                              out objId
                            );
                          if (fldCode != "")
                          {
                            // Print the ObjectId
                            ed.WriteMessage(
                              "\nFound Object ID: "
                              + objId.ToString()
                            );
                            obj =
                              tr.GetObject(
                                objId,
                                OpenMode.ForRead
                              );
                            // ... and the type of the object
                            ed.WriteMessage(
                              ", which is an object of type "
                              + obj.GetType().ToString()
                            );
                          }
                        } while (fldCode != "");                          
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    // Extract an ObjectId from a field string
    // and return the remainder of the string
    //
    static public string FindObjectId(
      string text,
      out ObjectId objId
    )
    {
      const string prefix = "%<\\_ObjId ";
      const string suffix = ">%";

      // Find the location of the prefix string
      int preLoc = text.IndexOf(prefix);
      if (preLoc > 0)
      {
        // Find the location of the ID itself
        int idLoc = preLoc + prefix.Length;

        // Get the remaining string
        string remains = text.Substring(idLoc);

        // Find the location of the suffix
        int sufLoc = remains.IndexOf(suffix);

        // Extract the ID string and get the ObjectId
        string id = remains.Remove(sufLoc);
        objId = new ObjectId(Convert.ToInt32(id));

        // Return the remainder, to allow extraction
        // of any remaining IDs
        return remains.Substring(sufLoc + suffix.Length);
      }
      else
      {
        objId = ObjectId.Null;
        return "";
      }
    }
  }
}

Here's what happens when we run the code. Firstly I went and created a simple, closed polyline and a circle. I then created a single MText object with field codes accessing the other two objects' areas:
0.jpg
I then run the GFL command and select the MText object:
Command: GFL
Select an MText object containing field(s):
Field code: Area of the circle: \AcObjProp Object(%<\_ObjId
2130239616>%).Area\P\PArea of the polyline: \AcObjProp Object(%<\_ObjId
2130239624>%).Area
Found Object ID: (2130239616), which is an object of type
Autodesk.AutoCAD.DatabaseServices.Circle
Found Object ID: (2130239624), which is an object of type
Autodesk.AutoCAD.DatabaseServices.Polyline

As you can see, we've been able to find and extract information from the objects referred to by fields in an MText object.


论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2025-1-6 00:49 , Processed in 0.437465 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表