My main purpose is to replace all blocks named "8x11Materiel" in my current drawing with the drawing "C:/8x11Materiel.dwg".
So I made a code that deleted all blocks "8x11Materiel", stored their positions, and purged all purgeable items of the drawing. But when it comes to inserting the drawing "C:/8x11Materiel.dwg" at the stored positions, I'm not able to produce or find a code that works.
I found out how to do it using AutoLisp using the following line: (command "insert" "C:/8x11Materiel.dwg" '(0 0) 1 1 ""), but in C# it seems to be more complicated.
Thank you in advance for your time.
My current code just in case you need a bit more context:
moderator edit: Put code into code window. Please use the </> button.
private readonly List<Point3d> _deletedBlockPositions = new List<Point3d>(); // Global list to store deleted block positions
private readonly List<Scale3d> _deletedBlockScaleFactors = new List<Scale3d>(); // Global list to store deleted block scale factors
[CommandMethod("Replace8x11MaterielBlocks")]
public void Replace8x11MaterielBlocks()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
{
Application.ShowAlertDialog("No active document found.");
return;
}
Database db = doc.Database;
Editor editor = doc.Editor;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)transaction.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord modelSpace = (BlockTableRecord)transaction.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
// Process all entities in the model space
foreach (ObjectId objId in modelSpace)
{
ProcessEntity(transaction, objId); // Deletes 8x11MaterielBlocks
}
// Commit the transaction
transaction.Commit();
}
// Now, purge the block definitions
using (Transaction purgeTransaction = db.TransactionManager.StartTransaction())
{
BlockTable blockTable = (BlockTable)purgeTransaction.GetObject(db.BlockTableId, OpenMode.ForRead);
foreach (ObjectId objId in blockTable)
{
PurgeEntity(purgeTransaction, objId); // Purge all purgeable entities
}
purgeTransaction.Commit();
}
// ADD CODE HERE THAT WOULD INSERT THE DWG FOUND IN filePath IN THE POSITIONS STORED IN _deletedBlockPositions
string filePath = "C:\\8x11Materiel.dwg";
}
private void ProcessEntity(Transaction tr, ObjectId entityId)
{
Entity entity = tr.GetObject(entityId, OpenMode.ForWrite) as Entity;
if (entity is BlockReference blkRef)
{
if (blkRef.Name == "8x11Materiel")
{
_deletedBlockPositions.Add(blkRef.Position);
_deletedBlockScaleFactors.Add(blkRef.ScaleFactors);
blkRef.Erase();
// No need to process further, as we're done with the attributes
return;
}
var blockTableRecord = tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
// Recursively process the entities within the block
foreach (ObjectId nestedEntityId in blockTableRecord)
{
ProcessEntity(tr, nestedEntityId);
}
}
}
private static void PurgeEntity(Transaction tr, ObjectId entityId)
{
if (entityId.ObjectClass.Name == "AcDbBlockTableRecord")
{
BlockTableRecord blockTableRecord = tr.GetObject(entityId, OpenMode.ForRead) as BlockTableRecord;
if (blockTableRecord.IsAnonymous || blockTableRecord.IsLayout || blockTableRecord.Name == "*Model_Space" || blockTableRecord.Name == "*Paper_Space")
{
// Don't purge special block table records
return;
}
// Check if the block is empty (no block references)
if (blockTableRecord.GetBlockReferenceIds(true, true).Count == 0)
{
blockTableRecord.UpgradeOpen();
blockTableRecord.Erase();
}
}
}
Solved! Go to Solution.
Solved by norman.yuan. Go to Solution.
Let's see if I understand your issue correctly (since we are Acad .NET API programmers, when referring "block", make sure only use "block" while there is no confusion between block definition and block reference, otherwise always use "BlockReference/block reference" and "BlockTableRecord/block definition"):
You have a block definition "8x11Materiel" in current drawing, with many block references of it inserted. Now, you have an UPDATED block drawing, which redefines/updates to block definition. You want all the block references in the current drawing to be the references of new block definition, while their insertion position remain unchanged.
If that is the task you need to do, then it is rather simple: no need to erase all the block references, then purge the block definition, then insert the new/updated block definition, then insert block references in remembered location. No, there is no need to do these tedious work at all. The only thing you need to do is to update the block definition.
If the "8x11Materiel.dwg" is a block drawing, then you only need to call Database.Insert() to bring the new block definition with the same block name into the current drawing. This would effectively update the existing block definition with the same block name in the current drawing; in turn, the block references would be automatically updated (you would need to call Editor.Regen() if you want to see the change visually right away).
If the source drawing contains multiple block definitions, then you would use Database.WblockCloneObjects() to bring the needed new block definition into current drawing (using DuplicateRecordCloning.Replace argument).
It looks to me that you only need to use Database.Insert() to get the job done, only in a few lines of code (not tested, but you get the idea):
[CommandMethod("UpdateBlk")]
public void UpdateBlock()
{
var doc=Application.DocumentManager.MdiActiveDocument;
var ed=doc.Editor;
var db=doc.Database;
var blkFile="c:\\temp\\8X11Material.dwg"
var blkName=System.IO.Path.GetFileNameWithoutExtension(blkFile);
// read the block file into a database
using (var sourceDb=new Database(false, true))
{
sourceDb.ReadDwgFromFile(blkFile, FileOpenModeReadAndAllShare, false, null);
db.Insert(blkName, sourceDb, false)
}
ed.Regen();
}
@bsaidiDG51 , @Et al
Please use the 'Insert/Edit code sample' button when posting code.
Regards,
// Called Kerry in my other life.
Everything will work just as you expect it to, unless your expectations are incorrect.
class keyThumper<T> : Lazy<T>; another Swamper
Yes, you correctly understood my issue.
Now when I try your code, it works only for the "physical/visual" aspect of the block (like an mtext change or a removed line), but for the attributes of the block no change takes place. The block that I try to insert has different attribute definitions than the ones existing in the drawing (additional attributes and some attributes with a different tag). So when the program inserts the new block definition, the visual aspect of the block gets updated but it keeps the same attributes as the blocks that were "replaced".
Thank you again for your time.
The used code:
[CommandMethod("UpdateBlk")]
public void UpdateBlock()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
var db = doc.Database;
var blkFile = "C:\\8x11Materiel.dwg";
var blkName = System.IO.Path.GetFileNameWithoutExtension(blkFile);
// read the block file into a database
using (var sourceDb = new Database(false, true))
{
sourceDb.ReadDwgFile(blkFile, FileOpenMode.OpenForReadAndAllShare, false, null);
db.Insert(blkName, sourceDb, false);
}
ed.Regen();
}
In your original post, you did not mention the block reference has attributes, nor the code in your post indicate that.
I assume you fully understand that when you say attribute, it is the AttributeReference(s) that are created with AttributeDefinition in the block definition as TEMPLATE,AFTER a BlockReference is created, and they ONLY BELONG TO the block reference.
So, after you inserted new/updated block definition into the current drawing, which results in all the block references of it being updated GEOMETRICALLY/VISUALLY (such as color/linetype/layer of the element entities in the block definition), HOWEVER, it does not update block references' attributes because the block definition has now knowledge of the AttributeReferences of the block reference. It is up to you to decide what to do with the AttributeReferences. You could run the ExpressTool's ATTSYC command to reset attributes of the block references due to block definition update, if the new block definition has the same set of attributes defined in it.
But since you say the new block definition has different attribute defined, then, you need to handle this with your own code after the new bock definition is brought into the drawing:
1. find and loop through all BlockReferences of the block;
2. For each BlockReference, you can simply erase all the AttributeReferences
3. Recreate AttributeRefernces according to newly updated block definition (BlockTableRecord) (I assume you do know how to create AttributeReference based on a block definition).
I have a solution for changing title blocks that is similar to what you want to do.
Can't find what you're looking for? Ask the community or share your knowledge.