This interesting question came up in our discussion forums:
Does anyone have a routine that will insert all the drawings from a single folder into one drawing to create a legend sheet? I'm trying to document the company's various blocks and details for dissemination amongst several offices.
The simplest – and most elegant, in my opinion – approach for addressing this requirement is via the Table object, which allows you to include block thumbnails in each of its cells. So we would need to import the various drawings into the current drawing as blocks, and then point the various cells of the table object at each of them, in turn. Thankfully I could borrow a good amount of the code to do this from these previous posts.
Here’s the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows;
using System.IO;
using System.Linq;
namespace MythsAndLegends
{
public class Commands
{
[CommandMethod("LEGEND")]
public void CreateLegend()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// Ask the user to select the drawings to list in the table
OpenFileDialog ofd =
new OpenFileDialog(
"Select drawings to add to legend",
null,
"dwg",
"DrawingsToImport",
OpenFileDialog.OpenFileDialogFlags.AllowMultiple
);
System.Windows.Forms.DialogResult dr = ofd.ShowDialog();
if (dr != System.Windows.Forms.DialogResult.OK)
return;
// Get the list of filenames and say how many were selected
string[] names = ofd.GetFilenames();
ed.WriteMessage(
"\n{0} files selected.", names.Length
);
// If we have some selected, we'll create a table
if (names.Length > 0)
{
int numRows, numCols;
// If only one was selected, we know the table will
// be 1x1
if (names.Length == 1)
{
numRows = 1;
numCols = 1;
}
else
{
// Otherwise we prompt for the number of columns
PromptIntegerOptions opts =
new PromptIntegerOptions(
"\nEnter number of columns: "
);
opts.LowerLimit = 1;
opts.UpperLimit = names.Length;
opts.DefaultValue = 1;
opts.UseDefaultValue = true;
PromptIntegerResult pir = ed.GetInteger(opts);
if (pir.Status != PromptStatus.OK)
return;
// Get whether to start at the top or the bottom
PromptKeywordOptions pko =
new PromptKeywordOptions(
"\nPopulate from the top or from the bottom?"
);
pko.AllowNone = true;
pko.Keywords.Add("Top");
pko.Keywords.Add("Bottom");
pko.Keywords.Default = "Top";
PromptResult pkr = ed.GetKeywords(pko);
bool fromTop = (pkr.StringResult == "Top");
// Get whether to start at the top or the bottom
pko =
new PromptKeywordOptions(
"\nList alphabetically or in selected order?"
);
pko.AllowNone = true;
pko.Keywords.Add("Alphabetically");
pko.Keywords.Add("Selected");
pko.Keywords.Default = "Alphabetically";
pkr = ed.GetKeywords(pko);
bool sort = (pkr.StringResult == "Alphabetically");
// And the insertion point of the table
PromptPointResult pr =
ed.GetPoint("\nEnter table insertion point: ");
if (pr.Status == PromptStatus.OK)
{
Transaction tr =
doc.TransactionManager.StartTransaction();
using (tr)
{
// Use constants for the cell dimensions
const int cellWidth = 3;
const int cellHeight = 3;
// Create the table
Table tb = new Table();
tb.TableStyle = db.Tablestyle;
// We can calculate the number of rows based on
// the number of cells and the number of columns
numCols = pir.Value;
numRows = (names.Length / numCols) + 1;
// We'll add in our custom columns and rows
if (numCols > 0)
{
tb.InsertColumns(1, cellWidth, numCols);
}
if (numRows > 0)
{
tb.InsertRows(1, cellHeight, numRows);
}
// And then delete the original row/column
// that comes with the blank table
tb.DeleteRows(0, 1);
tb.DeleteColumns(0, 1);
tb.Position = pr.Value;
if (sort)
{
names = names.OrderBy(x => x).ToArray<string>();
}
// Loop through the names, adding them to the table
for (int i = 0; i < names.Length; i++)
{
string blockName = "";
try
{
// Use a Database to insert a block for each
// drawing into the current drawing
using (Database src = new Database(false, true))
{
// First we read in the DWG
src.ReadDwgFile(
names[i], FileShare.Read, true, ""
);
// Take the name of the file without the
// extension
blockName =
Path.GetFileNameWithoutExtension(names[i]);
// Check whether it works as a symbol table
// name (will thrown an exception if not)
SymbolUtilityServices.ValidateSymbolName(
blockName, false
);
// Insert our drawing as a block (which
// will take the modelspace)
ObjectId blockId =
db.Insert(blockName, src, false);
// Calculate the row and column for this item
// If from the top: we just divide by the
// number of columns to get the row
// If from the bottom, subtract from
// the total rows
int row =
(fromTop ?
i / numCols :
numRows - (i / numCols + 1)
);
// The column is just the modulus remainder
int col = i % numCols;
// Insert the block as the contents of our cell
Cell cell = tb.Cells[row, col];
cell.Contents.InsertAt(0);
cell.Contents[0].BlockTableRecordId =
blockId;
}
}
catch (System.Exception ex)
{
ed.WriteMessage(
"\nCould not add \"{0}\": {1}",
blockName, ex.Message
);
}
}
tb.GenerateLayout();
// Finally we add our table to modelspace
BlockTable bt =
(BlockTable)tr.GetObject(
doc.Database.BlockTableId,
OpenMode.ForRead
);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
bt[BlockTableRecord.ModelSpace],
OpenMode.ForWrite
);
btr.AppendEntity(tb);
tr.AddNewlyCreatedDBObject(tb, true);
tr.Commit();
}
}
}
}
}
}
}
When we run the LEGEND command, we get asked to select some drawings:
We then get prompted with the number of drawings selected and get asked to provide some additional information to determine how the table gets created. We’ll run the command twice, selecting the same set of AutoCAD sample drawings but using 3 and 4 columns, populating the two tables from the top and the bottom respectively.
Command: LEGEND
13 files selected.
Enter number of columns <1>: 3
Populate from the top or from the bottom? [Top/Bottom] <Top>: Top
List alphabetically or in selected order? [Alphabetically/Selected]
<Alphabetically>: Alphabetically
Enter table insertion point:
[Dump of block imports deleted...]
Command: LEGEND
13 files selected.
Enter number of columns <1>: 4
Populate from the top or from the bottom? [Top/Bottom] <Top>: Bottom
List alphabetically or in selected order? [Alphabetically/Selected]
<Alphabetically>: Alphabetically
Enter table insertion point:
[Dump of block imports deleted...]
We can see that the tables contain the same order of blocks, but the first is wider and was populated from the top-left rather than the bottom-left: