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(...);
Dictionarydic=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 (KetValuePairitem 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 Dictionarydic)
{
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, Dictionarydic)
{
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.
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.
ReplyDeleteYes, 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().
ReplyDelete