明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 12075|回复: 10

[Kean专集] Kean专题(7)—Fields

   关闭 [复制链接]
发表于 2009-5-22 21:36 | 显示全部楼层 |阅读模式
本帖最后由 作者 于 2009-5-23 8:56:40 编辑

原帖:http://through-the-interface.typepad.com/through_the_interface/fields/
一、在表格中嵌入字段
June 11, 2007
Embedding fields in an AutoCAD table using .NET
This post is in response to a few requests I've received to show how to embed fields in tables. It follows on from this previous post, where we looked at how to embed a block preview icon in a table. The technique I'm showing in this post is far from being specific to tables, however. You could very easily use it for any other text object that supports fields inside AutoCAD (MText, Text, Attributes, etc.)
Fields are very cool objects: they allow you to access a very wide array of information and embed them inside textual objects in AutoCAD. A field is an object, but can be defined by a text string containing angled-brackets etc. that AutoCAD interprets and regenerates at different times during execution (on regen, on drawing load etc. - check out the FIELDEVAL system variable for more details on that). The simplest way to create field objects in your applications is simply to embed a correctly formatted string: all being well, AutoCAD will understand it to represent a field and will create the corresponding object behind the scenes. Easy! :-)
To understand in detail how the field mechanism works, I'd suggest checking out the TextFileField sample on the ObjectARX SDK. This is a C++ sample I created back when fields were first introduced (2005, if I recall correctly), to show how to implement your own custom field. This particular one links to a text file and embeds the file's contents in the container object.
There are various standard fields available inside AutoCAD. You can access document-level information (such as the author of the drawing) or system-level information (such as the current date). To get started with fields, I recommend using the FIELD command to bring up the field-definition dialog (also accessible from the MTEXT toolbar, among other places). This dialog allows you to configure the vast majority of fields, including formatting codes, which are not explicitly documented.

Of the various standard fields that come with AutoCAD, the one I find most appealing - as a programmer - is the AcObjProp field, which provides the ability to access COM properties from a field object. This is really very cool - you basically pass in the object ID of the object you'd like to access, and the COM Automation property you'd like to read, and the field does the rest. This opens up enormous possibilities, as it ultimately allows you access to *any* object property, assuming it's exposed through COM (and developers often expose their custom objects via COM as it allows integration with the Property Palette and the ability to manipulate these objects via VBA).
Let's look at the string we'd like to add to our code. The plan is to add a column to our table that includes a boolean (Yes/No) indicating whether the block definition is dynamic, or not.
Here's an example of a string we can embed directly in the table cell to do this:
  1. %<\AcObjProp Object(%<\_ObjId 2130399456>%).IsDynamicBlock>%
复制代码
Breaking it down:
The first and last two characters tell AutoCAD the extents of the field definition
The AcObjProp string tells AutoCAD we're dealing with an object property
The Object string tells AutoCAD we're about to refer to an object
The _ObjId field points to an object by it's internal Object ID
The property we want to access is IsDynamicBlock
Object IDs are only valid for a particular session, so this number can never be hard-coded. AutoCAD is clever enough to remap these throughout the drawing whenever it is loaded, however, which allows you to reopen drawings and the properties still to be accessible by fields.
If you were to see this field embedded in a text object, it would display a grey box containing either "0" or "1", the results of calling the IsDynamicBlock property for a particular block definition (which is what needs to be pointed at by that particular Object ID, by the way). This isn't ideal, as we'd like to use a text string. You can apply formatting codes to the results of the AcObjProp field, by specifying /f. The specific codes - as mentioned previously - are not documented, but the FIELD command allows you to find out what they should be. The trick is to find a property that is of the same datatype as yours, and copy the formatting code.
For instance, I used the MText.BackgroundFill property (also a Boolean) to help me work out that the formatting code I need for my property is "%bl2". Here's the FIELD dialog showing me this information:

So I now know that we want to add the following string (with the Object ID changed appropriately) for each of our blocks:
  1. %<\AcObjProp Object(%<\_ObjId 2130399456>%).IsDynamicBlock \f "%bl2">%
复制代码
Alright, we're finally ready for some code... :-)
Here's the updated C# code. I haven't numbered the lines, this time, as that makes me lose a few valuable columns of width, but the changed code should be easy enough to identify - it's simply adding an additional column to our previous table (although I did put some lines in to add some column headings):
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. namespace TableCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("CRT")]
  11.     static public void CreateTable()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       PromptPointResult pr =
  18.         ed.GetPoint("\nEnter table insertion point: ");
  19.       if (pr.Status == PromptStatus.OK)
  20.       {
  21.         Transaction tr =
  22.           doc.TransactionManager.StartTransaction();
  23.         using (tr)
  24.         {
  25.           BlockTable bt =
  26.             (BlockTable)tr.GetObject(
  27.               doc.Database.BlockTableId,
  28.               OpenMode.ForRead
  29.             );
  30.           Table tb = new Table();
  31.           tb.TableStyle = db.Tablestyle;
  32.           tb.NumRows = 5;
  33.           // Added an additional column for the block image
  34.           // and one for the "is dynamic" flag
  35.           tb.NumColumns = 5;
  36.           tb.SetRowHeight(3);
  37.           tb.SetColumnWidth(15);
  38.           tb.Position = pr.Value;
  39.           // Create a 2-dimensional array
  40.           // of our table contents
  41.           string[,] str = new string[5, 4];
  42.           str[0, 0] = "Part No.";
  43.           str[0, 1] = "Name ";
  44.           str[0, 2] = "Material ";
  45.           str[1, 0] = "1876-1";
  46.           str[1, 1] = "Flange";
  47.           str[1, 2] = "Perspex";
  48.           str[2, 0] = "0985-4";
  49.           str[2, 1] = "Bolt";
  50.           str[2, 2] = "Steel";
  51.           str[3, 0] = "3476-K";
  52.           str[3, 1] = "Tile";
  53.           str[3, 2] = "Ceramic";
  54.           str[4, 0] = "8734-3";
  55.           str[4, 1] = "Kean";
  56.           str[4, 2] = "Mostly water";
  57.           // Use a nested loop to add and format each cell
  58.           for (int i = 0; i < 5; i++)
  59.           {
  60.             for (int j = 0; j < 3; j++)
  61.             {
  62.               tb.SetTextHeight(i, j, 1);
  63.               tb.SetTextString(i, j, str[i, j]);
  64.               tb.SetAlignment(i, j, CellAlignment.MiddleCenter);
  65.             }
  66.             // Adding title information for additional columns
  67.             if (i == 0)
  68.             {
  69.               tb.SetTextHeight(i, 3, 1);
  70.               tb.SetTextString(i, 3, "Block Preview");
  71.               tb.SetAlignment(i, 3, CellAlignment.MiddleCenter);
  72.               tb.SetTextHeight(i, 4, 1);
  73.               tb.SetTextString(i, 4, "Is Dynamic?");
  74.               tb.SetAlignment(i, 4, CellAlignment.MiddleCenter);
  75.             }
  76.             // If a block definition exists for a block of our
  77.             // "name" field, then let's set it in the 4th column
  78.             if (bt.Has(str[i, 1]))
  79.             {
  80.               ObjectId objId = bt[str[i, 1]];
  81.               tb.SetBlockTableRecordId(i, 3, objId, true);
  82.               // And then we use a field to check on whether
  83.               // it's a dynamic block or not
  84.               string strObjId = objId.ToString();
  85.               strObjId = strObjId.Trim(new char[] {'(',')'});
  86.               tb.SetTextHeight(i, 4, 1);
  87.               tb.SetTextString(
  88.                 i,
  89.                 4,
  90.                 "%<\\AcObjProp Object(%<\\_ObjId "
  91.                   + strObjId
  92.                   +">%).IsDynamicBlock \\f "%bl2">%"
  93.               );
  94.               tb.SetAlignment(i, 4, CellAlignment.MiddleCenter);
  95.             }
  96.           }
  97.           tb.GenerateLayout();
  98.           BlockTableRecord btr =
  99.             (BlockTableRecord)tr.GetObject(
  100.               bt[BlockTableRecord.ModelSpace],
  101.               OpenMode.ForWrite
  102.             );
  103.           btr.AppendEntity(tb);
  104.           tr.AddNewlyCreatedDBObject(tb, true);
  105.           tr.Commit();
  106.         }
  107.       }
  108.     }
  109.   }
  110. }
And here are the results of running the CRT command, assuming the KEAN block is the only dynamic one of the four:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-22 21:50 | 显示全部楼层
二、创建一个块属性列表
June 14, 2007
Creating a table of block attributes in AutoCAD using .NET - Part 1
This post was inspired by suggestions from a few different people (you know who you are! :-). I'm going to take it in two parts: this post will focus on creating a table automatically that lists the values of attribute references included in block references in the modelspace that point to a particular block table record selected by the user. Phew. The next post will add some functionality to create a "total" of one of the columns in the table we create, by using a table formula that performs a sum of the appropriate cells.
The below code is actually quite similar in behaviour to the Table sample on the ObjectARX SDK and also the EATTEXT command inside AutoCAD - both of which will help you create tables from block attributes. I wrote this code in AutoCAD 2007 (and it should work just fine in 2008, also). I haven't tested against prior versions.
One item of note is the ability to either embed or link the data placed in the table. "Embedding" means we just take a copy of the attribute values and place them as plain text in the cells; "linking" means we use a field to create a reference from the cell to the attribute's value (using the technique shown in the previous post).
The code is quite lengthy, but I've done my best to comment it to make it more clear what's going on. Here's the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Specialized;
  7. using System;
  8. namespace TableCreation
  9. {
  10.   public class Commands
  11.   {
  12.     // Set up some formatting constants
  13.     // for the table
  14.     const double colWidth = 15.0;
  15.     const double rowHeight = 3.0;
  16.     const double textHeight = 1.0;
  17.     const CellAlignment cellAlign =
  18.       CellAlignment.MiddleCenter;
  19.     // Helper function to set text height
  20.     // and alignment of specific cells,
  21.     // as well as inserting the text
  22.     static public void SetCellText(
  23.       Table tb,
  24.       int row,
  25.       int col,
  26.       string value
  27.     )
  28.     {
  29.       tb.SetAlignment(row, col, cellAlign);
  30.       tb.SetTextHeight(row, col, textHeight);
  31.       tb.SetTextString(row, col, value);
  32.     }
  33.     [CommandMethod("BAT")]
  34.     static public void BlockAttributeTable()
  35.     {
  36.       Document doc =
  37.         Application.DocumentManager.MdiActiveDocument;
  38.       Database db = doc.Database;
  39.       Editor ed = doc.Editor;
  40.       // Ask for the name of the block to find
  41.       PromptStringOptions opt =
  42.         new PromptStringOptions(
  43.           "\nEnter name of block to list: "
  44.         );
  45.       PromptResult pr = ed.GetString(opt);
  46.       if (pr.Status == PromptStatus.OK)
  47.       {
  48.         string blockToFind =
  49.           pr.StringResult.ToUpper();
  50.         bool embed = false;
  51.         // Ask whether to embed or link the data
  52.         PromptKeywordOptions pko =
  53.           new PromptKeywordOptions(
  54.             "\nEmbed or link the attribute values: "
  55.           );
  56.         pko.AllowNone = true;
  57.         pko.Keywords.Add("Embed");
  58.         pko.Keywords.Add("Link");
  59.         pko.Keywords.Default = "Embed";
  60.         PromptResult pkr =
  61.           ed.GetKeywords(pko);
  62.         if (pkr.Status == PromptStatus.None ||
  63.             pkr.Status == PromptStatus.OK)
  64.         {
  65.           if (pkr.Status == PromptStatus.None ||
  66.               pkr.StringResult == "Embed")
  67.             embed = true;
  68.           else
  69.             embed = false;
  70.         }
  71.         Transaction tr =
  72.           doc.TransactionManager.StartTransaction();
  73.         using (tr)
  74.         {
  75.           // Let's check the block exists
  76.           BlockTable bt =
  77.             (BlockTable)tr.GetObject(
  78.               doc.Database.BlockTableId,
  79.               OpenMode.ForRead
  80.             );
  81.           if (!bt.Has(blockToFind))
  82.           {
  83.             ed.WriteMessage(
  84.               "\nBlock "
  85.               + blockToFind
  86.               + " does not exist."
  87.             );
  88.           }
  89.           else
  90.           {
  91.             // And go through looking for
  92.             // attribute definitions
  93.             StringCollection colNames =
  94.               new StringCollection();
  95.             BlockTableRecord bd =
  96.               (BlockTableRecord)tr.GetObject(
  97.                 bt[blockToFind],
  98.                 OpenMode.ForRead
  99.               );
  100.             foreach (ObjectId adId in bd)
  101.             {
  102.               DBObject adObj =
  103.                 tr.GetObject(
  104.                   adId,
  105.                   OpenMode.ForRead
  106.                 );
  107.               // For each attribute definition we find...
  108.               AttributeDefinition ad =
  109.                 adObj as AttributeDefinition;
  110.               if (ad != null)
  111.               {
  112.                 // ... we add its name to the list
  113.                 colNames.Add(ad.Tag);
  114.               }
  115.             }
  116.             if (colNames.Count == 0)
  117.             {
  118.               ed.WriteMessage(
  119.                 "\nThe block "
  120.                 + blockToFind
  121.                 + " contains no attribute definitions."
  122.               );
  123.             }
  124.             else
  125.             {
  126.               // Ask the user for the insertion point
  127.               // and then create the table
  128.               PromptPointResult ppr =
  129.                 ed.GetPoint(
  130.                   "\nEnter table insertion point: "
  131.                 );
  132.               if (ppr.Status == PromptStatus.OK)
  133.               {
  134.                 Table tb = new Table();
  135.                 tb.TableStyle = db.Tablestyle;
  136.                 tb.NumRows = 1;
  137.                 tb.NumColumns = colNames.Count;
  138.                 tb.SetRowHeight(rowHeight);
  139.                 tb.SetColumnWidth(colWidth);
  140.                 tb.Position = ppr.Value;
  141.                 // Let's add our column headings
  142.                 for (int i = 0; i < colNames.Count; i++)
  143.                 {
  144.                   SetCellText(tb, 0, i, colNames[i]);
  145.                 }
  146.                 // Now let's search for instances of
  147.                 // our block in the modelspace
  148.                 BlockTableRecord ms =
  149.                   (BlockTableRecord)tr.GetObject(
  150.                     bt[BlockTableRecord.ModelSpace],
  151.                     OpenMode.ForRead
  152.                   );
  153.                 int rowNum = 1;
  154.                 foreach (ObjectId objId in ms)
  155.                 {
  156.                   DBObject obj =
  157.                     tr.GetObject(
  158.                       objId,
  159.                       OpenMode.ForRead
  160.                     );
  161.                   BlockReference br =
  162.                     obj as BlockReference;
  163.                   if (br != null)
  164.                   {
  165.                     BlockTableRecord btr =
  166.                       (BlockTableRecord)tr.GetObject(
  167.                         br.BlockTableRecord,
  168.                         OpenMode.ForRead
  169.                       );
  170.                     using (btr)
  171.                     {
  172.                       if (btr.Name.ToUpper() == blockToFind)
  173.                       {
  174.                         // We have found one of our blocks,
  175.                         // so add a row for it in the table
  176.                         tb.InsertRows(
  177.                           rowNum,
  178.                           rowHeight,
  179.                           1
  180.                         );
  181.                         // Assume that the attribute refs
  182.                         // follow the same order as the
  183.                         // attribute defs in the block
  184.                         int attNum = 0;
  185.                         foreach (
  186.                           ObjectId arId in
  187.                           br.AttributeCollection
  188.                         )
  189.                         {
  190.                           DBObject arObj =
  191.                             tr.GetObject(
  192.                               arId,
  193.                               OpenMode.ForRead
  194.                             );
  195.                           AttributeReference ar =
  196.                             arObj as AttributeReference;
  197.                           if (ar != null)
  198.                           {
  199.                             // Embed or link the values
  200.                             string strCell;
  201.                             if (embed)
  202.                             {
  203.                               strCell = ar.TextString;
  204.                             }
  205.                             else
  206.                             {
  207.                               string strArId =
  208.                                 arId.ToString();
  209.                               strArId =
  210.                                 strArId.Trim(
  211.                                   new char[] { '(', ')' }
  212.                                 );
  213.                               strCell =
  214.                                 "%<\\AcObjProp Object("
  215.                                   + "%<\\_ObjId "
  216.                                   + strArId
  217.                                   + ">%).TextString>%";
  218.                             }
  219.                             SetCellText(
  220.                               tb,
  221.                               rowNum,
  222.                               attNum,
  223.                               strCell
  224.                             );
  225.                           }
  226.                           attNum++;
  227.                         }
  228.                         rowNum++;
  229.                       }
  230.                     }
  231.                   }
  232.                 }
  233.                 tb.GenerateLayout();
  234.                 ms.UpgradeOpen();
  235.                 ms.AppendEntity(tb);
  236.                 tr.AddNewlyCreatedDBObject(tb, true);
  237.                 tr.Commit();
  238.               }
  239.             }
  240.           }
  241.         }
  242.       }
  243.     }
  244.   }
  245. }
To test the code, I created a block called "DATA" containing attribute definitions for NAME, PARTNUM, MATERIAL and COST. I then created blocks corresponding to the data we had previously hard-coded.
Here's what happens when I used the BAT command on this data to create two tables - the top with "embedded" values, the bottom with them "linked":

In the next post we'll add a "Total" row at the bottom of the table, and use a formula to calculate the sum of a particular column (the logical one being COST, as it's numerical).

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-22 21:55 | 显示全部楼层
June 18, 2007
Creating a table of block attributes in AutoCAD using .NET - Part 2
In the last post we looked at some code to create a table of attribute values for a particular block. In this post we'll extend that code and show how to use a formula to create a total of those values.
Below is the C# code. I've numbered the lines, and those in red are new since the last post. The complete source file can be downloaded here.
Firstly, a quick breakdown of the changes:
Lines 60-81 deal with user input, and the forcing of the decision to "embed" rather than "link", if we're performing the total (table formulae do not work with fields, even if they have numeric results, so we're forced to create the table with the current value, rather than a field pointing to the attribute reference)
Line 134 and subsequently lines 159-166 declare and set a variable indicating for which column we're going to provide a total
Lines 169-181 deal with the exceptional case that we don't find the specified attribute definition
Lines 310-336 create our additional row, and insert the total in the appropriate cell. We're using a formula such as this: %<\AcExpr (Sum(A2:A4)) \f "%lu2%pr2">%
The \f flag specifies we want a numeric value with 2 decimal places - these codes are not documented, but you can find them out by using the FIELD command, as described in this previous post
Line 343 performs a regen, to update the value of our field
And now for the code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Specialized;
  7. using System;
  8. namespace TableCreation
  9. {
  10.   public class Commands
  11.   {
  12.     // Set up some formatting constants
  13.     // for the table
  14.     const double colWidth = 15.0;
  15.     const double rowHeight = 3.0;
  16.     const double textHeight = 1.0;
  17.     const CellAlignment cellAlign =
  18.       CellAlignment.MiddleCenter;
  19.     // Helper function to set text height
  20.     // and alignment of specific cells,
  21.     // as well as inserting the text
  22.     static public void SetCellText(
  23.       Table tb,
  24.       int row,
  25.       int col,
  26.       string value
  27.     )
  28.     {
  29.       tb.SetAlignment(row, col, cellAlign);
  30.       tb.SetTextHeight(row, col, textHeight);
  31.       tb.SetTextString(row, col, value);
  32.     }
  33.     [CommandMethod("BAT")]
  34.     static public void BlockAttributeTable()
  35.     {
  36.       Document doc =
  37.         Application.DocumentManager.MdiActiveDocument;
  38.       Database db = doc.Database;
  39.       Editor ed = doc.Editor;
  40.       // Ask for the name of the block to find
  41.       PromptStringOptions opt =
  42.         new PromptStringOptions(
  43.           "\nEnter name of block to list: "
  44.         );
  45.       PromptResult pr = ed.GetString(opt);
  46.       if (pr.Status == PromptStatus.OK)
  47.       {
  48.         string blockToFind =
  49.           pr.StringResult.ToUpper();
  50.         bool embed = false;
  51.         // And the attribute to provide total for
  52.         opt.Message =
  53.           "\nEnter name of column to total <"">: ";
  54.         pr = ed.GetString(opt);
  55.         if (pr.Status == PromptStatus.None ||
  56.             pr.Status == PromptStatus.OK)
  57.         {
  58.           string columnToTotal =
  59.               pr.StringResult.ToUpper();
  60.           if (columnToTotal != "")
  61.           {
  62.             // If a column has been chosen, we need
  63.             // to embed the attribute values
  64.             // as otherwise the "sum" formula will fail
  65.             embed = true;
  66.           }
  67.           else
  68.           {
  69.             // Ask whether to embed or link the data
  70.             PromptKeywordOptions pko =
  71.               new PromptKeywordOptions(
  72.                 "\nEmbed or link the attribute values: "
  73.               );
  74.             pko.AllowNone = true;
  75.             pko.Keywords.Add("Embed");
  76.             pko.Keywords.Add("Link");
  77.             pko.Keywords.Default = "Embed";
  78.             PromptResult pkr =
  79.               ed.GetKeywords(pko);
  80.             if (pkr.Status == PromptStatus.None ||
  81.                 pkr.Status == PromptStatus.OK)
  82.             {
  83.               if (pkr.Status == PromptStatus.None ||
  84.                   pkr.StringResult == "Embed")
  85.                 embed = true;
  86.               else
  87.                 embed = false;
  88.             }
  89.           }
  90.           Transaction tr =
  91.             doc.TransactionManager.StartTransaction();
  92.           using (tr)
  93.           {
  94.             // Let's check the block exists
  95.             BlockTable bt =
  96.               (BlockTable)tr.GetObject(
  97.                 doc.Database.BlockTableId,
  98.                 OpenMode.ForRead
  99.               );
  100.             if (!bt.Has(blockToFind))
  101.             {
  102.               ed.WriteMessage(
  103.                 "\nBlock "
  104.                 + blockToFind
  105.                 + " does not exist."
  106.               );              
  107.             }
  108.             else
  109.             {
  110.               // And go through looking for
  111.               // attribute definitions
  112.               StringCollection colNames =
  113.                 new StringCollection();
  114.               int colToTotalIdx = -1;
  115.               BlockTableRecord bd =
  116.                 (BlockTableRecord)tr.GetObject(
  117.                   bt[blockToFind],
  118.                   OpenMode.ForRead
  119.                 );
  120.               foreach (ObjectId adId in bd)
  121.               {
  122.                 DBObject adObj =
  123.                   tr.GetObject(
  124.                     adId,
  125.                     OpenMode.ForRead
  126.                   );
  127.                 // For each attribute definition we find...
  128.                 AttributeDefinition ad =
  129.                   adObj as AttributeDefinition;
  130.                 if (ad != null)
  131.                 {
  132.                   // ... we add its name to the list
  133.                   colNames.Add(ad.Tag);
  134.                   if (ad.Tag.ToUpper() == columnToTotal)
  135.                   {
  136.                     // Save the index of the column
  137.                     // we want to total
  138.                     colToTotalIdx =
  139.                       colNames.Count - 1;
  140.                   }
  141.                 }
  142.               }
  143.               // If we didn't find the attribute to be totalled
  144.               // then simply ignore the request and continue
  145.               if (columnToTotal != "" && colToTotalIdx < 0)
  146.               {
  147.                 ed.WriteMessage(
  148.                   "\nAttribute definition for "
  149.                   + columnToTotal
  150.                   + " not found in "
  151.                   + blockToFind
  152.                   + ". Total will not be added to the table."
  153.                 );
  154.               }
  155.               if (colNames.Count == 0)
  156.               {
  157.                 ed.WriteMessage(
  158.                   "\nThe block "
  159.                   + blockToFind
  160.                   + " contains no attribute definitions."                  
  161.                 );
  162.               }
  163.               else
  164.               {
  165.                 // Ask the user for the insertion point
  166.                 // and then create the table
  167.                 PromptPointResult ppr =
  168.                   ed.GetPoint(
  169.                     "\nEnter table insertion point: "
  170.                   );
  171.                 if (ppr.Status == PromptStatus.OK)
  172.                 {
  173.                   Table tb = new Table();
  174.                   tb.TableStyle = db.Tablestyle;
  175.                   tb.NumRows = 1;
  176.                   tb.NumColumns = colNames.Count;
  177.                   tb.SetRowHeight(rowHeight);
  178.                   tb.SetColumnWidth(colWidth);
  179.                   tb.Position = ppr.Value;
  180.                   // Let's add our column headings
  181.                   for (int i = 0; i < colNames.Count; i++)
  182.                   {
  183.                     SetCellText(tb, 0, i, colNames[i]);
  184.                   }
  185.                   // Now let's search for instances of
  186.                   // our block in the modelspace
  187.                   BlockTableRecord ms =
  188.                     (BlockTableRecord)tr.GetObject(
  189.                       bt[BlockTableRecord.ModelSpace],
  190.                       OpenMode.ForRead
  191.                     );
  192.                   int rowNum = 1;
  193.                   foreach (ObjectId objId in ms)
  194.                   {
  195.                     DBObject obj =
  196.                       tr.GetObject(
  197.                         objId,
  198.                         OpenMode.ForRead
  199.                       );
  200.                     BlockReference br =
  201.                       obj as BlockReference;
  202.                     if (br != null)
  203.                     {
  204.                       BlockTableRecord btr =
  205.                         (BlockTableRecord)tr.GetObject(
  206.                           br.BlockTableRecord,
  207.                           OpenMode.ForRead
  208.                         );
  209.                       using (btr)
  210.                       {
  211.                         if (btr.Name.ToUpper() == blockToFind)
  212.                         {
  213.                           // We have found one of our blocks,
  214.                           // so add a row for it in the table
  215.                           tb.InsertRows(
  216.                               rowNum,
  217.                               rowHeight,
  218.                               1
  219.                           );
  220.                           // Assume that the attribute refs
  221.                           // follow the same order as the
  222.                           // attribute defs in the block
  223.                           int attNum = 0;
  224.                           foreach (
  225.                             ObjectId arId in
  226.                             br.AttributeCollection
  227.                           )
  228.                           {
  229.                             DBObject arObj =
  230.                               tr.GetObject(
  231.                                 arId,
  232.                                 OpenMode.ForRead
  233.                               );
  234.                             AttributeReference ar =
  235.                               arObj as AttributeReference;
  236.                             if (ar != null)
  237.                             {
  238.                               // Embed or link the values
  239.                               string strCell;
  240.                               if (embed)
  241.                               {
  242.                                 strCell = ar.TextString;
  243.                               }
  244.                               else
  245.                               {
  246.                                 string strArId =
  247.                                   arId.ToString();
  248.                                 strArId =
  249.                                   strArId.Trim(
  250.                                     new char[] { '(', ')' }
  251.                                   );
  252.                                 strCell =
  253.                                   "%<\\AcObjProp Object("
  254.                                     + "%<\\_ObjId "
  255.                                     + strArId
  256.                                     + ">%).TextString>%";
  257.                               }
  258.                               SetCellText(
  259.                                 tb,
  260.                                 rowNum,
  261.                                 attNum,
  262.                                 strCell
  263.                               );
  264.                             }
  265.                             attNum++;
  266.                           }
  267.                           rowNum++;
  268.                         }
  269.                       }
  270.                     }
  271.                   }
  272.                   // Now let's add a row for our total
  273.                   if (colToTotalIdx >= 0)
  274.                   {
  275.                     tb.InsertRows(rowNum, rowHeight, 1);
  276.                     char colLetter =
  277.                       Convert.ToChar(
  278.                         (Convert.ToInt32(
  279.                           'A') + colToTotalIdx
  280.                         )
  281.                       );
  282.                     // Add a formula to sum the column
  283.                     SetCellText(
  284.                       tb,
  285.                       rowNum,
  286.                       colToTotalIdx,
  287.                       "%<\\AcExpr (Sum("
  288.                         + colLetter
  289.                         + "2:"
  290.                         + colLetter
  291.                         + rowNum.ToString()
  292.                         + ")) \\f  "%lu2%pr2">%"
  293.                     );
  294.                   }
  295.                   tb.GenerateLayout();
  296.                   ms.UpgradeOpen();
  297.                   ms.AppendEntity(tb);
  298.                   tr.AddNewlyCreatedDBObject(tb, true);
  299.                   tr.Commit();
  300.                   ed.Regen();
  301.                 }
  302.               }
  303.             }
  304.           }
  305.         }
  306.       }
  307.     }
  308.   }
  309. }
Here's what happens when you run the updated BAT command against the data I used last time:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-22 21:59 | 显示全部楼层
三、用字段连接图元属性
July 13, 2007
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:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System;
  6. namespace FieldExtraction
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("GFL")]
  11.     static public void GetFieldLink()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       // Ask the user to select an attribute or an mtext
  18.       PromptEntityOptions opt =
  19.         new PromptEntityOptions(
  20.           "\nSelect an MText object containing field(s): "
  21.         );
  22.       opt.SetRejectMessage(
  23.         "\nObject must be MText."
  24.       );
  25.       opt.AddAllowedClass(typeof(MText), false);
  26.       PromptEntityResult res =
  27.         ed.GetEntity(opt);
  28.       if (res.Status == PromptStatus.OK)
  29.       {
  30.         Transaction tr =
  31.           doc.TransactionManager.StartTransaction();
  32.         using (tr)
  33.         {
  34.           // Check the entity is an MText object
  35.           DBObject obj =
  36.             tr.GetObject(
  37.               res.ObjectId,
  38.               OpenMode.ForRead
  39.             );
  40.           MText mt = obj as MText;
  41.           if (mt != null)
  42.           {
  43.             if (!mt.HasFields)
  44.             {
  45.               ed.WriteMessage(
  46.                 "\nMText object does not contain fields."
  47.               );
  48.             }
  49.             else
  50.             {
  51.               // Open the extension dictionary
  52.               DBDictionary extDict =
  53.                 (DBDictionary)tr.GetObject(
  54.                   mt.ExtensionDictionary,
  55.                   OpenMode.ForRead
  56.                 );
  57.               const string fldDictName = "ACAD_FIELD";
  58.               const string fldEntryName = "TEXT";
  59.               // Get the field dictionary
  60.               if (extDict.Contains(fldDictName))
  61.               {
  62.                 ObjectId fldDictId =
  63.                   extDict.GetAt(fldDictName);
  64.                 if (fldDictId != ObjectId.Null)
  65.                 {
  66.                   DBDictionary fldDict =
  67.                     (DBDictionary)tr.GetObject(
  68.                       fldDictId,
  69.                       OpenMode.ForRead
  70.                     );
  71.                   // Get the field itself
  72.                   if (fldDict.Contains(fldEntryName))
  73.                   {
  74.                     ObjectId fldId =
  75.                       fldDict.GetAt(fldEntryName);
  76.                     if (fldId != ObjectId.Null)
  77.                     {
  78.                       obj =
  79.                         tr.GetObject(
  80.                           fldId,
  81.                           OpenMode.ForRead
  82.                         );
  83.                       Field fld = obj as Field;
  84.                       if (fld != null)
  85.                       {
  86.                         // And finally get the string
  87.                         // including the field codes
  88.                         string fldCode = fld.GetFieldCode();
  89.                         ed.WriteMessage(
  90.                           "\nField code: "
  91.                           + fldCode
  92.                         );
  93.                         // Loop, using our helper function
  94.                         // to find the object references
  95.                         do
  96.                         {
  97.                           ObjectId objId;
  98.                           fldCode =
  99.                             FindObjectId(
  100.                               fldCode,
  101.                               out objId
  102.                             );
  103.                           if (fldCode != "")
  104.                           {
  105.                             // Print the ObjectId
  106.                             ed.WriteMessage(
  107.                               "\nFound Object ID: "
  108.                               + objId.ToString()
  109.                             );
  110.                             obj =
  111.                               tr.GetObject(
  112.                                 objId,
  113.                                 OpenMode.ForRead
  114.                               );
  115.                             // ... and the type of the object
  116.                             ed.WriteMessage(
  117.                               ", which is an object of type "
  118.                               + obj.GetType().ToString()
  119.                             );
  120.                           }
  121.                         } while (fldCode != "");                          
  122.                       }
  123.                     }
  124.                   }
  125.                 }
  126.               }
  127.             }
  128.           }
  129.         }
  130.       }
  131.     }
  132.     // Extract an ObjectId from a field string
  133.     // and return the remainder of the string
  134.     //
  135.     static public string FindObjectId(
  136.       string text,
  137.       out ObjectId objId
  138.     )
  139.     {
  140.       const string prefix = "%<\\_ObjId ";
  141.       const string suffix = ">%";
  142.       // Find the location of the prefix string
  143.       int preLoc = text.IndexOf(prefix);
  144.       if (preLoc > 0)
  145.       {
  146.         // Find the location of the ID itself
  147.         int idLoc = preLoc + prefix.Length;
  148.         // Get the remaining string
  149.         string remains = text.Substring(idLoc);
  150.         // Find the location of the suffix
  151.         int sufLoc = remains.IndexOf(suffix);
  152.         // Extract the ID string and get the ObjectId
  153.         string id = remains.Remove(sufLoc);
  154.         objId = new ObjectId(Convert.ToInt32(id));
  155.         // Return the remainder, to allow extraction
  156.         // of any remaining IDs
  157.         return remains.Substring(sufLoc + suffix.Length);
  158.       }
  159.       else
  160.       {
  161.         objId = ObjectId.Null;
  162.         return "";
  163.       }
  164.     }
  165.   }
  166. }
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:

I then run the GFL command and select the MText object:
  1. Command: GFL
  2. Select an MText object containing field(s):
  3. Field code: Area of the circle: \AcObjProp Object(%<\_ObjId
  4. 2130239616>%).Area\P\PArea of the polyline: \AcObjProp Object(%<\_ObjId
  5. 2130239624>%).Area
  6. Found Object ID: (2130239616), which is an object of type
  7. Autodesk.AutoCAD.DatabaseServices.Circle
  8. Found Object ID: (2130239624), which is an object of type
  9. 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.

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 09:44 | 显示全部楼层

Kean专题(8)—Tables

原帖:http://through-the-interface.typepad.com/through_the_interface/tables/
一、创建表格
June 06, 2007
Creating an AutoCAD table using .NET
This suggestion came in a few weeks ago from Kélcyo Pereira, and I've borrowed some code from Sreekar Devatha, from DevTech India, to help implement the suggestion.
The following C# code creates a very simple table and inserts it at the position selected by the user. The table is really very simply - a 5 (row) x 3 (column) table created from string values, no other data-types. It picks up the current style and aligns each cell as "middle, center". That's really all there is to it.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. namespace TableCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("CRT")]
  11.     static public void CreateTable()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       PromptPointResult pr =
  18.         ed.GetPoint("\nEnter table insertion point: ");
  19.       if (pr.Status == PromptStatus.OK)
  20.       {
  21.         Table tb = new Table();
  22.         tb.TableStyle = db.Tablestyle;
  23.         tb.NumRows = 5;
  24.         tb.NumColumns = 3;
  25.         tb.SetRowHeight(3);
  26.         tb.SetColumnWidth(15);
  27.         tb.Position = pr.Value;
  28.         // Create a 2-dimensional array
  29.         // of our table contents
  30.         string[,] str = new string[5, 3];
  31.         str[0, 0] = "Part No.";
  32.         str[0, 1] = "Name ";
  33.         str[0, 2] = "Material ";
  34.         str[1, 0] = "1876-1";
  35.         str[1, 1] = "Flange";
  36.         str[1, 2] = "Perspex";
  37.         str[2, 0] = "0985-4";
  38.         str[2, 1] = "Bolt";
  39.         str[2, 2] = "Steel";
  40.         str[3, 0] = "3476-K";
  41.         str[3, 1] = "Tile";
  42.         str[3, 2] = "Ceramic";
  43.         str[4, 0] = "8734-3";
  44.         str[4, 1] = "Kean";
  45.         str[4, 2] = "Mostly water";
  46.         // Use a nested loop to add and format each cell
  47.         for (int i = 0; i < 5; i++)
  48.         {
  49.           for (int j = 0; j < 3; j++)
  50.           {
  51.             tb.SetTextHeight(i, j, 1);
  52.             tb.SetTextString(i, j, str[i, j]);
  53.             tb.SetAlignment(i, j, CellAlignment.MiddleCenter);
  54.           }
  55.         }
  56.         tb.GenerateLayout();
  57.         Transaction tr =
  58.           doc.TransactionManager.StartTransaction();
  59.         using (tr)
  60.         {
  61.           BlockTable bt =
  62.             (BlockTable)tr.GetObject(
  63.               doc.Database.BlockTableId,
  64.               OpenMode.ForRead
  65.             );
  66.           BlockTableRecord btr =
  67.             (BlockTableRecord)tr.GetObject(
  68.               bt[BlockTableRecord.ModelSpace],
  69.               OpenMode.ForWrite
  70.             );
  71.           btr.AppendEntity(tb);
  72.           tr.AddNewlyCreatedDBObject(tb, true);
  73.           tr.Commit();
  74.         }
  75.       }
  76.     }
  77.   }
  78. }
And here's what you see when you run the CRT command and select a point:

I'd like to take this further by showing more advanced concepts around tables - please post a comment if you have a particular suggestion or request.

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 09:48 | 显示全部楼层
二、在表格中显示块定义的预览图像
June 08, 2007
Creating an AutoCAD table containing block images using .NET
Further to the previous post showing the creation of a simple table, this shows how to add a column that contains a preview image of a particular block definition.
I had to modify the code somewhat to open the block table sooner than we did before (I tend not to leave it open for longer than I have to, but in this case we want to check it for appropriate block definitions earlier on, while we're creating the table). Then I added a simple check, to see whether a block definition corresponding to our "name" field exists for a particular row, and if so, we add a reference to the block table record in the 4th column of the table.
Here's the updated C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. namespace TableCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("CRT")]
  11.     static public void CreateTable()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       PromptPointResult pr =
  18.         ed.GetPoint("\nEnter table insertion point: ");
  19.       if (pr.Status == PromptStatus.OK)
  20.       {
  21.         Transaction tr =
  22.           doc.TransactionManager.StartTransaction();
  23.         using (tr)
  24.         {
  25.           BlockTable bt =
  26.             (BlockTable)tr.GetObject(
  27.               doc.Database.BlockTableId,
  28.               OpenMode.ForRead
  29.             );
  30.           Table tb = new Table();
  31.           tb.TableStyle = db.Tablestyle;
  32.           tb.NumRows = 5;
  33.           // Added an additional column for the block image
  34.           tb.NumColumns = 4;
  35.           tb.SetRowHeight(3);
  36.           tb.SetColumnWidth(15);
  37.           tb.Position = pr.Value;
  38.           // Create a 2-dimensional array
  39.           // of our table contents
  40.           string[,] str = new string[5, 3];
  41.           str[0, 0] = "Part No.";
  42.           str[0, 1] = "Name ";
  43.           str[0, 2] = "Material ";
  44.           str[1, 0] = "1876-1";
  45.           str[1, 1] = "Flange";
  46.           str[1, 2] = "Perspex";
  47.           str[2, 0] = "0985-4";
  48.           str[2, 1] = "Bolt";
  49.           str[2, 2] = "Steel";
  50.           str[3, 0] = "3476-K";
  51.           str[3, 1] = "Tile";
  52.           str[3, 2] = "Ceramic";
  53.           str[4, 0] = "8734-3";
  54.           str[4, 1] = "Kean";
  55.           str[4, 2] = "Mostly water";
  56.           // Use a nested loop to add and format each cell
  57.           for (int i = 0; i < 5; i++)
  58.           {
  59.             for (int j = 0; j < 3; j++)
  60.             {
  61.               tb.SetTextHeight(i, j, 1);
  62.               tb.SetTextString(i, j, str[i, j]);
  63.               tb.SetAlignment(i, j, CellAlignment.MiddleCenter);
  64.             }
  65.             // If a block definition exists for a block of our
  66.             // "name" field, then let's set it in the 4th column
  67.             if (bt.Has(str[i, 1]))
  68.             {
  69.               tb.SetBlockTableRecordId(i, 3, bt[str[i, 1]], true);
  70.             }
  71.           }
  72.           tb.GenerateLayout();
  73.           BlockTableRecord btr =
  74.             (BlockTableRecord)tr.GetObject(
  75.               bt[BlockTableRecord.ModelSpace],
  76.               OpenMode.ForWrite
  77.             );
  78.           btr.AppendEntity(tb);
  79.           tr.AddNewlyCreatedDBObject(tb, true);
  80.           tr.Commit();
  81.         }
  82.       }
  83.     }
  84.   }
  85. }
And here's what you see if you run the CRT command with some blocks in the current drawing called "FLANGE", "TILE", "BOLT" and "KEAN":

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 09:54 | 显示全部楼层
三、创建一个链接Excel工作表的表格
August 22, 2007
Creating an AutoCAD table linked to an Excel spreadsheet using .NET
In the last post I promised to tackle this issue, and so here we are again. :-)
Note: the code in this post relies on enhanced table functionality introduced in AutoCAD 2008, so please don't get frustrated trying to make this work in previous versions.
The following C# code follows on from yesterday's, taking the spreadsheet selected by the user and linking it to a newly-created table in the active AutoCAD drawing. I haven't bothered with line numbering in the below code, as it follows on almost exactly from the code shown last time (aside from renaming the namespace, the command and the function, as well as adding a string constant at the top of the function implementation).
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Windows;
  6. namespace LinkToExcel
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("TFS")]
  11.     static public void TableFromSpreadsheet()
  12.     {
  13.       // Hardcoding the string
  14.       // Could also select for it
  15.       const string dlName =
  16.         "Import table from Excel demo";
  17.       Document doc =
  18.         Application.DocumentManager.MdiActiveDocument;
  19.       Database db = doc.Database;
  20.       Editor ed = doc.Editor;
  21.       OpenFileDialog ofd =
  22.         new OpenFileDialog(
  23.           "Select Excel spreadsheet to link",
  24.           null,
  25.           "xls; xlsx",
  26.           "ExcelFileToLink",
  27.           OpenFileDialog.OpenFileDialogFlags.
  28.             DoNotTransferRemoteFiles
  29.         );
  30.       System.Windows.Forms.DialogResult dr =
  31.         ofd.ShowDialog();
  32.       if (dr != System.Windows.Forms.DialogResult.OK)
  33.         return;
  34.       ed.WriteMessage(
  35.         "\nFile selected was "{0}".",
  36.         ofd.Filename
  37.       );
  38.       PromptPointResult ppr =
  39.         ed.GetPoint(
  40.           "\nEnter table insertion point: "
  41.         );
  42.       if (ppr.Status != PromptStatus.OK)
  43.         return;
  44.       // Remove the Data Link, if it exists already
  45.       DataLinkManager dlm = db.DataLinkManager;
  46.       ObjectId dlId = dlm.GetDataLink(dlName);
  47.       if (dlId != ObjectId.Null)
  48.       {
  49.         dlm.RemoveDataLink(dlId);
  50.       }
  51.       // Create and add the Data Link
  52.       DataLink dl = new DataLink();
  53.       dl.DataAdapterId = "AcExcel";
  54.       dl.Name = dlName;
  55.       dl.Description =
  56.         "Excel fun with Through the Interface";
  57.       dl.ConnectionString = ofd.Filename;
  58.       dl.DataLinkOption =
  59.         DataLinkOption.PersistCache;
  60.       dl.UpdateOption |=
  61.         (int)UpdateOption.AllowSourceUpdate;
  62.       dlId = dlm.AddDataLink(dl);
  63.       Transaction tr =
  64.         doc.TransactionManager.StartTransaction();
  65.       using (tr)
  66.       {
  67.         tr.AddNewlyCreatedDBObject(dl, true);
  68.         BlockTable bt =
  69.           (BlockTable)tr.GetObject(
  70.             db.BlockTableId,
  71.             OpenMode.ForRead
  72.           );
  73.         Table tb = new Table();
  74.         tb.TableStyle = db.Tablestyle;
  75.         tb.Position = ppr.Value;
  76.         tb.SetDataLink(0, 0, dlId, true);
  77.         tb.GenerateLayout();
  78.         BlockTableRecord btr =
  79.           (BlockTableRecord)tr.GetObject(
  80.             db.CurrentSpaceId,
  81.             OpenMode.ForWrite
  82.           );
  83.         btr.AppendEntity(tb);
  84.         tr.AddNewlyCreatedDBObject(tb, true);
  85.         tr.Commit();
  86.       }
  87.       // Force a regen to display the table
  88.       ed.Regen();
  89.     }
  90.   }
  91. }
Here's what happens when you run the TFS command and select your favourite XLS for linking (I used mass-balance.xls from AutoCAD 2008's Sample\Mechanical Sample folder):

At this stage I haven't focused at all on formating - this is just coming in "as is", without any adjustment of cell alignments, column widths or row heights.
I chose to hardcode the name of the Data Link we use for the spreadsheet. You can run the DATALINK command to check on it, after the command has executed:

It doesn't seem to be an issue if you repeat the command and bring in a different spreadsheet using the same link - the link appears to continue (although I haven't performed exhaustive testing). If it does prove to be a problem it should be simple enough to create a unique Data Link per spreadsheet imported (or even per time the command is run).

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 10:22 | 显示全部楼层
三、从Excel工作表更新表格
August 24, 2007
Updating an AutoCAD table linked to an Excel spreadsheet using .NET
Thanks to Viru Aithal, from DevTech India, for providing the code for this post (I converted the C# code below from some C++ he had sent to a developer).
In the last post we showed how to create a table linked to an Excel spreadsheet using .NET in AutoCAD 2008. AutoCAD does a great job of looking for changes in the Excel spreadsheet, and asking whether you want to update the linked table:

There may be times, however, when you want to force the update programmatically, whether from the spreadsheet to the table ot vice-versa. In this post we'll show the code to update the table from the spreadsheet, and in the next post we'll see some code to update the spreadsheet from the table (should it have been unlocked and edited).
Here's the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. namespace LinkToExcel
  6. {
  7.   public class Commands
  8.   {
  9.     [CommandMethod("S2T")]
  10.     static public void UpdateTableFromSpreadsheet()
  11.     {
  12.       Document doc =
  13.         Application.DocumentManager.MdiActiveDocument;
  14.       Database db = doc.Database;
  15.       Editor ed = doc.Editor;
  16.       PromptEntityOptions opt =
  17.         new PromptEntityOptions(
  18.           "\nSelect table to update: "
  19.         );
  20.       opt.SetRejectMessage(
  21.         "\nEntity is not a table."
  22.       );
  23.       opt.AddAllowedClass(typeof(Table), false);
  24.       PromptEntityResult per =
  25.         ed.GetEntity(opt);
  26.       if (per.Status != PromptStatus.OK)
  27.         return;
  28.       Transaction tr =
  29.         db.TransactionManager.StartTransaction();
  30.       using (tr)
  31.       {
  32.         try
  33.         {
  34.           DBObject obj =
  35.             tr.GetObject(
  36.               per.ObjectId,
  37.               OpenMode.ForRead
  38.             );
  39.           Table tb = (Table)obj;
  40.           // It should always be a table
  41.           // but we'll check, just in case
  42.           if (tb != null)
  43.           {
  44.             // The table must be open for write
  45.             tb.UpgradeOpen();
  46.             // Update the data link from the spreadsheet
  47.             ObjectId dlId = tb.GetDataLink(0, 0);
  48.             DataLink dl =
  49.               (DataLink)tr.GetObject(
  50.                 dlId,
  51.                 OpenMode.ForWrite
  52.               );
  53.             dl.Update(
  54.               UpdateDirection.SourceToData,
  55.               UpdateOption.None
  56.             );
  57.             // And the table from the data link
  58.             tb.UpdateDataLink(
  59.               UpdateDirection.SourceToData,
  60.               UpdateOption.None
  61.             );
  62.           }
  63.           tr.Commit();
  64.           ed.WriteMessage(
  65.             "\nUpdated the table from the spreadsheet."
  66.           );
  67.         }
  68.         catch (Exception ex)
  69.         {
  70.           ed.WriteMessage(
  71.             "\nException: {0}",
  72.             ex.Message
  73.           );
  74.         }
  75.       }
  76.     }
  77.   }
  78. }

When you run the S2T (for Spreadsheet-to-Table) command, you will be prompted to select a table. The code retrieves the link information from the table and then requests the data link to pull down new data from the spreadsheet before updating the table. Next time we'll look at the code for T2S...

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 10:28 | 显示全部楼层
四、从链接的AutoCad表格更新Excel工作表
August 27, 2007
Updating an Excel spreadsheet from a linked AutoCAD table using .NET
In the last post we saw some code to update an AutoCAD table linked to an Excel spreadsheet. In this post we go the other way, updating an Excel spreadsheet from a linked AutoCAD table.
Here's the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Windows;
  6. namespace LinkToExcel
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("T2S")]
  11.     static public void UpdateSpreadsheetFromTable()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       PromptEntityOptions opt =
  18.         new PromptEntityOptions(
  19.           "\nSelect table with spreadsheet to update: "
  20.         );
  21.       opt.SetRejectMessage(
  22.         "\nEntity is not a table."
  23.       );
  24.       opt.AddAllowedClass(typeof(Table), false);
  25.       PromptEntityResult per =
  26.         ed.GetEntity(opt);
  27.       if (per.Status != PromptStatus.OK)
  28.         return;
  29.       Transaction tr =
  30.         db.TransactionManager.StartTransaction();
  31.       using (tr)
  32.       {
  33.         try
  34.         {
  35.           DBObject obj =
  36.             tr.GetObject(
  37.               per.ObjectId,
  38.               OpenMode.ForRead
  39.             );
  40.           Table tb = (Table)obj;
  41.           // It should always be a table
  42.           // but we'll check, just in case
  43.           if (tb != null)
  44.           {
  45.             // The table must be open for write
  46.             tb.UpgradeOpen();
  47.             // Update the data link from the table
  48.             tb.UpdateDataLink(
  49.               UpdateDirection.DataToSource,
  50.               UpdateOption.ForceFullSourceUpdate
  51.             );
  52.             // And the spreadsheet from the data link
  53.             ObjectId dlId = tb.GetDataLink(0, 0);
  54.             DataLink dl =
  55.               (DataLink)tr.GetObject(
  56.                 dlId,
  57.                 OpenMode.ForWrite
  58.               );
  59.             dl.Update(
  60.               UpdateDirection.DataToSource,
  61.               UpdateOption.ForceFullSourceUpdate
  62.             );
  63.           }
  64.           tr.Commit();
  65.           ed.WriteMessage(
  66.             "\nUpdated the spreadsheet from the table."
  67.           );
  68.         }
  69.         catch (Exception ex)
  70.         {
  71.           ed.WriteMessage(
  72.             "\nException: {0}",
  73.             ex.Message
  74.           );
  75.         }
  76.       }
  77.     }
  78.   }
  79. }
Tables with linked spreadsheets are locked by default and display this glyph when you hover over them: . Before you run the code you will need to unlock the table by right-clicking the cell(s) you wish to edit:

One point to note is that the code will work even if the spreadsheet is open in Excel, but the contents will not be updated automatically - you have to close and reopen the file to see the results. And you will probably see this dialog come up twice:

For the best (most logical) results, the T2S command should really be run when the spreadsheet is not open in Excel. I expect it's possible to determine whether a spreadsheet is open in Excel from using standard file access functions in .NET (requesting exclusive access, to see whether it's possible to get it), but that's being left as an exercise for the reader (or for another day, at least :-).
For your convenience, here's a source file containing the code from the last three posts (command implementations for TFS, S2T and T2S).

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-23 10:35 | 显示全部楼层
五、创建自定义表格样式
November 03, 2008
Creating a custom AutoCAD table style using .NET
The question of how to define a new table style programmatically has come up a couple of times over the last week or so, so this post shows how to approach this. I've used the code from this previous post as a basis for this post's.
The important thing to know is that TableStyle objects are stored in a special dictionary, which can be accessed via a Database's TableStyleDictionaryId property. This ObjectId property allows you to access the dictionary, from which you can query the contents, determine whether your style exists and add a new one if it doesn't. The code to specify the colour and formatting of the table style is reasonably straightforward.
Here's some C# code to define a new "Garish Table Style" with a red header/title area, yellow data area and magenta text throughout (better put on your sunglasses before running it... :-):
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Colors;
  6. namespace TableAndStyleCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("CTWS")]
  11.     static public void CreateTableWithStyle()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       PromptPointResult pr =
  18.         ed.GetPoint("\nEnter table insertion point: ");
  19.       if (pr.Status == PromptStatus.OK)
  20.       {
  21.         Transaction tr =
  22.           doc.TransactionManager.StartTransaction();
  23.         using (tr)
  24.         {
  25.           // First let us create our custom style,
  26.           //  if it doesn't exist
  27.           const string styleName = "Garish Table Style";
  28.           ObjectId tsId = ObjectId.Null;
  29.           DBDictionary sd =
  30.             (DBDictionary)tr.GetObject(
  31.               db.TableStyleDictionaryId,
  32.               OpenMode.ForRead
  33.             );
  34.           // Use the style if it already exists
  35.           if (sd.Contains(styleName))
  36.           {
  37.             tsId = sd.GetAt(styleName);
  38.           }
  39.           else
  40.           {
  41.             // Otherwise we have to create it
  42.             TableStyle ts = new TableStyle();
  43.             // Make the header area red
  44.             ts.SetBackgroundColor(
  45.               Color.FromColorIndex(ColorMethod.ByAci, 1),
  46.               (int)(RowType.HeaderRow | RowType.TitleRow)
  47.             );
  48.             // And the data area yellow
  49.             ts.SetBackgroundColor(
  50.               Color.FromColorIndex(ColorMethod.ByAci, 2),
  51.               (int)RowType.DataRow
  52.             );
  53.             // With magenta text everywhere (yeuch :-)
  54.             ts.SetColor(
  55.               Color.FromColorIndex(ColorMethod.ByAci, 6),
  56.               (int)(RowType.HeaderRow |
  57.                     RowType.TitleRow |
  58.                     RowType.DataRow)
  59.             );
  60.             // Add our table style to the dictionary
  61.             //  and to the transaction
  62.             sd.UpgradeOpen();
  63.             tsId = sd.SetAt(styleName, ts);
  64.             tr.AddNewlyCreatedDBObject(ts, true);
  65.             sd.DowngradeOpen();
  66.           }
  67.           BlockTable bt =
  68.             (BlockTable)tr.GetObject(
  69.               doc.Database.BlockTableId,
  70.               OpenMode.ForRead
  71.             );
  72.           Table tb = new Table();
  73.           // Use our table style
  74.           if (tsId == ObjectId.Null)
  75.             // This should not happen, unless the
  76.             //  above logic changes
  77.             tb.TableStyle = db.Tablestyle;
  78.           else
  79.             tb.TableStyle = tsId;
  80.           tb.NumRows = 5;
  81.           tb.NumColumns = 3;
  82.           tb.SetRowHeight(3);
  83.           tb.SetColumnWidth(15);
  84.           tb.Position = pr.Value;
  85.           // Create a 2-dimensional array
  86.           // of our table contents
  87.           string[,] str = new string[5, 4];
  88.           str[0, 0] = "Part No.";
  89.           str[0, 1] = "Name ";
  90.           str[0, 2] = "Material ";
  91.           str[1, 0] = "1876-1";
  92.           str[1, 1] = "Flange";
  93.           str[1, 2] = "Perspex";
  94.           str[2, 0] = "0985-4";
  95.           str[2, 1] = "Bolt";
  96.           str[2, 2] = "Steel";
  97.           str[3, 0] = "3476-K";
  98.           str[3, 1] = "Tile";
  99.           str[3, 2] = "Ceramic";
  100.           str[4, 0] = "8734-3";
  101.           str[4, 1] = "Kean";
  102.           str[4, 2] = "Mostly water";
  103.           // Use a nested loop to add and format each cell
  104.           for (int i = 0; i < 5; i++)
  105.           {
  106.             for (int j = 0; j < 3; j++)
  107.             {
  108.               tb.SetTextHeight(i, j, 1);
  109.               tb.SetTextString(i, j, str[i, j]);
  110.               tb.SetAlignment(i, j, CellAlignment.MiddleCenter);
  111.             }
  112.           }
  113.           tb.GenerateLayout();
  114.           BlockTableRecord btr =
  115.             (BlockTableRecord)tr.GetObject(
  116.               bt[BlockTableRecord.ModelSpace],
  117.               OpenMode.ForWrite
  118.             );
  119.           btr.AppendEntity(tb);
  120.           tr.AddNewlyCreatedDBObject(tb, true);
  121.           tr.Commit();
  122.         }
  123.       }
  124.     }
  125.   }
  126. }

Here's what happens when we load the module and run the CTWS command, selecting a location for our "garish" table:

And if we launch AutoCAD's TABLESTYLE command we can see our custom style in the list:

Update
A further post on this topic has now been published.

本帖子中包含更多资源

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

x
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-3-28 16:59 , Processed in 0.278486 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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