In C#, what would be the best way to get a block inserted at a specified point?
I am trying to get attributes from a block inserted at the endpoint of a polyline. The endpoints of the polyline and the blockname are known.
I just can not figure out how to get the block. Once I get the block I know how to get the attributes.
Thanks
Dan
Solved! Go to Solution.
Solved by _gile. Go to Solution.
With some help from this site, this is what I came up with... Wondering, if there is a better way?
using (Transaction tr = acCurDb.TransactionManager.StartTransaction()) { BlockTable acBlkTble = tr.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable; foreach (ObjectId btrObjId in acBlkTble) { BlockTableRecord btr = tr.GetObject(btrObjId, OpenMode.ForRead) as BlockTableRecord; ObjectIdCollection objIdColl = btr.GetBlockReferenceIds(true, true); foreach (ObjectId objId_loopVariable in objIdColl) { BlockReference blk = (BlockReference)tr.GetObject(objId_loopVariable, OpenMode.ForRead); blockName = blk.Name.ToUpper(); if (blockName.IndexOf("WIRE-END") != -1) { pntBlockOrigin = blk.Position; if (pntBlockOrigin == pntPolyBeg) { //get the attributes AttributeCollection attCol = blk.AttributeCollection; foreach (ObjectId attId in attCol) { AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead); switch (attRef.Tag.ToUpper()) { case "END-ID": attBegValue[0] = attRef.TextString; break; case "END-TERM": attBegValue[1] = attRef.TextString; break; } } } else if (pntBlockOrigin == pntPolyEnd) { //get the attributes AttributeCollection attCol = blk.AttributeCollection; foreach (ObjectId attId in attCol) { AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead); switch (attRef.Tag.ToUpper()) { case "END-ID": attEndValue[0] = attRef.TextString; break; case "END-TERM": attEndValue[1] = attRef.TextString; break; } } } } } } tr.Commit(); }
Create a pair of Point3D objects that define a window. P3D1 will be bottom right, and P3D2 will be top left. I use the term "aperture" - the last value I used was 20 metres. This creates a window that is 40 metres square, with your endpoint in the centre.
double aperture = 20.0;
Point3d P3D1 = new Point3d(yourpolylineendpoint.X - aperture, yourpolylineendpoint.Y - aperture, 0);
Point3d P3D2 = new Point3d(yourpolylineendpoint.X + aperture, yourpolylineendpoint.Y + aperture, 0);
Define a typedvalue and selection filter for "INSERT".
acdb.TypedValue[] values = { new acdb.TypedValue((int)acdb.DxfCode.Start, "INSERT") };
SelectionFilter sf = new SelectionFilter(values);
Create a prompt for select crossing window using your new points.
PromptSelectionResult psr = doc.Editor.SelectCrossingWindow(P3D1, P3D2, sf);
Check the status and see if there are any inserts within the window.
if (psr.Status == PromptStatus.OK && psr.Value.Count > 0)
{
acdb.ObjectIdCollection psrobjids = new acdb.ObjectIdCollection(psr.Value.GetObjectIds());
foreach (acdb.ObjectId oid in psrobjids)
{
acdb.BlockReference myblk = (acdb.BlockReference)tx.GetObject(oid, acdb.OpenMode.ForRead);
if (myblk.Name == "the block you are looking for")
{
// do what you need to do
// break;
}
}
}
<edit>
I am not sure if this way is any better than your last post. It looks like that works as well. Your way will work even if the block is not visible in the editor. I believe that object selection using window crossing implies that the object is visible in the editor window. If that's correct, your way is better.
</edit>
thanks for the reply... the only problem is I can not have anything else getting selected since my palette and the information it receives is based on a single selected polyline. It would create more of a headache iterating through the selected objects since the polyline needs to stay selected.
The user selection and your program selection are not the same. The user is not involved with (and does not see) the crossing window selection.
Hi,
The GetBlockReferenceIds() route is fine, but your code can be optimized:
- get directly the block table record in the block table using its name instead of scaning the whole block table ;
- exit the foreach loop as soon as a matching position block is found.
using (Transaction tr = acCurDb.TransactionManager.StartTransaction()) { BlockTable acBlkTble = tr.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable; // directly get the record in the table using its name BlockTableRecord btr = tr.GetObject(acBlkTble["WIRE-END"], OpenMode.ForRead) as BlockTableRecord; foreach (ObjectId objId_loopVariable in btr.GetBlockReferenceIds(true, true)) { BlockReference blk = (BlockReference)tr.GetObject(objId_loopVariable, OpenMode.ForRead); if (blk.Position == pntPolyBeg) { //get the attributes AttributeCollection attCol = blk.AttributeCollection; foreach (ObjectId attId in attCol) { AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead); switch (attRef.Tag.ToUpper()) { case "END-ID": attBegValue[0] = attRef.TextString; break; case "END-TERM": attBegValue[1] = attRef.TextString; break; } } break; // exit the foreach loop as soon as the block is found } else if (blk.Position == pntPolyEnd) { //get the attributes AttributeCollection attCol = blk.AttributeCollection; foreach (ObjectId attId in attCol) { AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead); switch (attRef.Tag.ToUpper()) { case "END-ID": attEndValue[0] = attRef.TextString; break; case "END-TERM": attEndValue[1] = attRef.TextString; break; } } break; // exit the foreach loop as soon as the block is found } } tr.Commit(); }
Just keep in mind that
btr.GetBlockReferenceIds(true, true)
will return a 0 on a dynamic block.
_gile himself talks about how to fetch dynamic blocks on a more recent post:
https://forums.autodesk.com/t5/net/blockreferenceids-is-sometimes-empty/m-p/7044001#M53047
Can't find what you're looking for? Ask the community or share your knowledge.