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:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
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
tb.NumColumns = 4;
tb.SetRowHeight(3);
tb.SetColumnWidth(15);
tb.Position = pr.Value;
// Create a 2-dimensional array
// of our table contents
string[,] str = new string[5, 3];
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);
}
// 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]))
{
tb.SetBlockTableRecordId(i, 3, bt[str[i, 1]], true);
}
}
tb.GenerateLayout();
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
bt[BlockTableRecord.ModelSpace],
OpenMode.ForWrite
);
btr.AppendEntity(tb);
tr.AddNewlyCreatedDBObject(tb, true);
tr.Commit();
}
}
}
}
}
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":


Subscribe via RSS
Muito bom este exemplo.
Aguardo um com a manipulação de campos, como criar e alterar campos no documento. Valeu.
" Very good this sample.
I wait one with the manipulation of Fields, as to create and to modify fields in the document.
It was valid."
Posted by: Kélcyo Pereira | June 09, 2007 at 11:18 PM
Kean, can you provide an example for putting another block into the same cell and changing cell layout (to horizontal layout or to vertical layout) in 2008?
Fields and formulas are of great interest too.
Posted by: Nikolay Poleshchuk | June 10, 2007 at 06:24 AM
A minha ideia é a seguinte:
1 - Criar um dialógo para selecionar um arquivo txt delimitado.
2 - E fazer a inserção dos valores na tabela.
Sei que isso eh possível no Autocad 2008, inserindo uma planilha do excel. Mais o interesse aqui é manipular via NET.
Com isso, teriamos abordado 3 conceitos.
- a utilização de diálogos no cad;
- a manipulação de dados externos vindo do arquivo txt; e
- a manipulação da tabela.
Valeu.
" My ideia is the following one:
1 - To create one dialógo to select an archive txt delimited.
2 - E to make the insertion of the values in the table.
I know that this eh possible in Autocad 2008, inserting a spread sheet of excel. More the interest is here to manipulate way NET.
With this, we teriamos boarded 3 concepts.
- the use of dialogue in cad;
- the manipulation of external data come of the archive txt;
- e manipulation of the table.
It was valid."
Posted by: Kélcyo Pereira | June 11, 2007 at 04:21 PM
Kélcyo,
Yes, I could certainly show this, but the pieces related to AutoCAD's APIs have already been shown in this code. The additional work (file selection and reading) are standard tasks that are relatively easy to accomplish with the .NET Framework (and I'm sure are covered by blog posts/articles elsewhere). Once you've populated your array of strings, the above code can be hooked in easily enough.
Thanks for the suggestion, though.
Regards,
Kean
Posted by: Kean | June 12, 2007 at 10:36 AM
OPPPS that's again gr8....
I did it in 2001 by using VB 6.. Thanks for this tut for Dot Net....
Sunilkumar S. (SUKU)
www.suncindia.com
Posted by: Sunilkumar Shinde | June 26, 2007 at 12:21 PM
hello i found your tutorial very informing but i have a problem applying it...
when i use the code for typical blocks it works perfectly but when the code works on a block that contains an attribute autocad crashes with an error - handle reentered...
i tried using the setblockattribute method in hope it will fix it but to no avail.
i would really apreciate if you could help because i have a complete addon program which is pending on this bug only
Posted by: blackneoo | June 09, 2008 at 06:08 PM
Yes... I see the same behaviour: the code wasn't designed to work with blocks with attributes.
I added some code, after the call to tb.SetBlockTableRecordId():
BlockTableRecord blockDef =
(BlockTableRecord)tr.GetObject(
objId,
OpenMode.ForRead
);
foreach (ObjectId id in blockDef)
{
DBObject obj =
tr.GetObject(id, OpenMode.ForRead);
AttributeDefinition att =
obj as AttributeDefinition;
if (att != null)
tb.SetBlockAttributeValue(i, 3, id, att.Tag);
}
This - as you've noticed - also fails, unless we change the transaction type by calling StartOpenCloseTransaction() instead of StartTransaction(). This creates a transaction type that (under the covers) uses Open & Close to access database-resident objects.
This type of transaction was added in 2009, so if you're using a previous version your only real option would be to translate all your calls to the transaction into individual ObjectId.Open() and ObjectId.Close() calls.
Assuming there's no way to get the standard transaction to work (the Open/Close Transaction was added to help deal with situations where finer grained access control is required by AutoCAD, and I suspect this is one of those situations).
Otherwise you might also try separating the table creation from the code to set the attribute values (by collecting the info in some kind of structure and adding it after the fact). This might also help.
Good luck,
Kean
Posted by: Kean | June 10, 2008 at 09:58 PM
Greeting Kean
thank you very much for replying
unfortunately am using autocad 2008 so i had to try replacing transaction calls with open\close methods and it worked great for the task but a side problem happened, it seems that something is not closed in the database so i cannot select the table and cannot save the file, after checking for a bit i found that i didn't close the table object, after i closed it i can select the table but any modification such as moving the table or trying to save the file i get an error eisalready in database.
here is the code for you so you can get a clear picture
this is a function i made that creates a table based on a filled array of blocks to generate a legend...
Public Sub Create_Table(ByVal RowCount As Integer, ByVal RowHeight As Double, ByVal ColumnWidth As Double, ByVal Position As Point3d, ByVal Symbols() As String)
Dim tb As Table = New Table
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Using db As Database = doc.Database
Using tr As Transaction = db.TransactionManager.StartTransaction
Dim Dlock As DocumentLock = Application.DocumentManager.MdiActiveDocument.LockDocument()
Dim bt As BlockTable = db.BlockTableId.Open(OpenMode.ForRead)
tb.TableStyle = db.Tablestyle
tb.NumRows = RowCount + 2
tb.NumColumns = 2
tb.SetRowHeight(RowHeight)
tb.SetColumnWidth(ColumnWidth)
tb.Position = Position
tb.SetTextHeight(RowType.DataRow, 2.5)
tb.SetTextHeight(RowType.HeaderRow, 2.5)
tb.SetTextHeight(RowType.TitleRow, 1)
tb.SetAlignment(CellAlignment.MiddleCenter, RowType.DataRow)
tb.SetAlignment(CellAlignment.MiddleCenter, RowType.HeaderRow)
tb.SetAlignment(CellAlignment.MiddleCenter, RowType.TitleRow)
tb.SetTextString(0, 0, "Legend")
tb.SetTextString(1, 0, "Symbol Name")
tb.SetTextString(1, 1, "Symbol Image")
Dim Row As Integer = 1
Dim i As Integer = 0
Dim Scaling_Factor As Double = 1
For i = 0 To RowCount
If bt.Has(Symbols(i)) Then
tb.SetBlockTableRecordId(Row, 0, bt(Symbols(i)), True)
Dim objid As ObjectId = tb.GetBlockTableRecordId(Row, 0, 0)
Dim blockdef As BlockTableRecord = objid.Open(OpenMode.ForRead)
For Each id As ObjectId In blockdef
Dim obj As DBObject = id.Open(OpenMode.ForRead)
If TypeOf (obj) Is AttributeDefinition Then
Dim att As AttributeDefinition = obj
tb.SetBlockAttributeValue(Row, 0, id, att.TextString)
End If
obj.Close()
Next
Row = Row + 1
blockdef.Close()
End If
Next i
tb.GenerateLayout()
Dim btr As BlockTableRecord = bt(BlockTableRecord.PaperSpace).Open(OpenMode.ForWrite)
'Dim btr As BlockTableRecord = tr.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite)
btr.AppendEntity(tb)
tr.Commit()
btr.Close()
bt.Close()
db.Dispose()
btr.Dispose()
bt.Dispose()
Dlock.Dispose()
tb.Close()
tb.Dispose()
tr.Dispose()
GC.Collect()
End Using
End Using
End Sub
Posted by: blackneoo | June 12, 2008 at 11:45 AM
One thing I can see straightaway is that you're still creating and committing the transaction - which is both unnecessary and potentially dangerous (you should avoid mixing transactions with open/close).
Other than that I can't see what the problem is, though you might try reducing the time the various objects are open (the block table could be closed sooner, if you collect the info you need and then close it, for instance).
Regards,
Kean
Posted by: Kean | June 12, 2008 at 01:06 PM
Greetings Kean
thanks again for your prompt reply
to tell you the truth i first tried it without using the transaction and i got an access violation error so when i used the transaction it worked...
for the tr.commit it was the only way for the table to render without the code ran and nothing happened...
for the block table i'll try to close it sooner and see what happens and as for the tb.close i cannot put it sooner than the document lock or i will get the already in database error at the moment the code is run..
btw i remember that the code ran correctly when the table doesn't have a blocks with attributes.
Posted by: blackneoo | June 12, 2008 at 08:20 PM
I am veteran in working Autocad. I like a lot what you have programmed. It is brilliant. I want to use it but i don t know how to.Can i convert it to autolisp? You can help me if you want.
Posted by: adreas | November 07, 2008 at 08:23 PM
It should be possible. Unfortunately I don't have time to spend on converting posts to other languages... someone on the AutoLISP Discussion Group may be able to help.
Kean
Posted by: Kean | November 10, 2008 at 09:05 AM