Tuesday, March 2, 2010

Retrieving and Updating Acad Block's Attributes with Managed ObjectARX API

Dealing block's attribute is one of the most common drafting task, naturally, it also the most common AutoCAD programming task.

Wandering amongst various AutoCAD programming related user groups online, I see there are questions on how to update/retrieve block's attributes raised/answered very often, again and again.

Here I post some code I use to update/retrieve block attribute. I use AutoCAD managed ObjectARX API.

Assumption: before the following code can be used, I assume the targeting block (BlockReference object) is known, e.g. the previous code has already found the tarting block refernce's ObjectId from a drawing's working database.



Document dwg=Application.DocumentManager.MdiDocument;
Database db=dwg.Database;

//code here to find taget block's ObjectId
ObjectId blkRefID=FindBlockReference(...);

Dictionary dic=new Dictionary();

//For each attribute in interest, add an item into
//the Dictionary with tag as key
dic.Add("TAG1","")
dic.Add("TAG2","")
dic.Add("TAG3","")
dic.Add("TAG4","")
dic.Add("TAG5","")

//Retrieve attribute value from the block
RetrieveBlockAttributeValues(db, blkRefID, ref dic);

//Show retrieved attribute value
foreach (KetValuePair item in dic)
{
dwg.Editor.WriteMessage(
"\nAttribute {0}={1}", item.Key, item.Value);
}

//Update attribute value
dic.Add("TAG1","AAAAA")
dic.Add("TAG2","BBBBB")
dic.Add("TAG3","CCCCC")
dic.Add("TAG4","DDDDD")
dic.Add("TAG5","EEEEE")

UpdateBlockAttribiuteValues(db, blkRefID, dic);






Here is the method to retrieve attribute values



private void RetrieveBlockAttributeValues(
Database db, ObjectId blkId, ref Dictionary dic)
{
using (Transaction tran = db.TransactionManager.StartTransaction())
{
//Get BlcolReference
BlockReference blockRef =
(BlockReference)tran.GetObject(blockRefId, OpenMode.ForRead);

foreach (ObjectId id in blockRef.AttributeCollection)
{
AttributeReference attRef =
(AttributeReference)tran.GetObject(id, OpenMode.ForRead);

if (dic.ContainsKey(attRef.Tag.ToUpper()))
{
dic[attRef.Tag.ToUpper()] = attRef.TextString;
}
}

tran.Commit();
}
}






Here is the method to update attribute values



private void UpdateBlockAttributeValues(
Database db, ObjectId blkId, Dictionary dic)
{
using (Transaction tran = db.TransactionManager.StartTransaction())
{
//Get BlcolReference
BlockReference blockRef =
(BlockReference)tran.GetObject(blockRefId, OpenMode.ForRead);

foreach (ObjectId id in blockRef.AttributeCollection)
{
AttributeReference attRef =
(AttributeReference)tran.GetObject(id, OpenMode.ForRead);

if (dic.ContainsKey(attRef.Tag.ToUpper()))
{
attRef.TextString=dic[attRef.Tag.ToUpper()];
}
}

tran.Commit();
}
}



As you can see the code to update and retrieve attributes' value to/from a block is pretty simple.

With a Dictionary object as the parameter used in the 2 methods of retrieving/updating attribute value, you are free to decide which attribute's value to retrieve/update. Say, a block has 10 attributes, named as "A", "B", "C"..., you can add 10 entry in the Dictionary with the 10 tags ("A", "B","C"...) as key, or you can only add 1 entry into the Dictionary, say, "A", should you are only interested in retrieve/update only that single attribute with tag as "A".

Another thing to pay attention is that Dictionary's Key (string) is case-sensitive. So, in my code I use string.ToUpper() to compare Dictionary's key with block attribute's tag when doing retrieving/updating.

From this code sample, one can realize that retrieving/updating block attribute value is an easy task as long as you know the block's tags in interest. If you can obtain a block's attribute tag list, you will never need to re-write code to retrieve/update block's attribute values (e.g. code in afore-mentioned 2 methods). In my next post, I'll talk about how to make block attribute configurable, so that you do not need to modify your code in your Acad applications due to possible changes made to a block/attributes.

2 comments:

Anonymous said...

This example really helped me, but there is a small error in te code, the attributereference should be openend as write instead of read, when tou want to update its values.

Norman Yuan said...

Yes, you are right. In the UpdateBlockAttributeValues() method, the AttributeReference should have been open for write. Better yet, it should still be open for read at first, and only after the "if dic.ContainsKey(...))" is true, then call attRef.UpgradeOpen().

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.