After several days, I can say I have reached a solution (yes I can say solution.... unbeliavable) suitable for my purpose.
Here the code for benefit of other.
I have to say the true, coding .net with Autocad is not for dummy and some of the code here is not clear to me at all (but works...)
In any case I leave the original code from @norman.yuan in which I edited and replaced with other fonts (ie: web, samples from official reference... suggestion by @ActivistInvestor, and my guess...) and adding some comment (hope they make sense).
The code doesn't replace ATTRIBUTES (the new block has to have own ATTRIBUTES in it, as suggested by @ActivistInvestor as faster way)
The code can transfer some attribute.text from the old block to th new one.
The last question:
Could someone explain to me how come the editor doesn't refresh the screen after each foreach operation?
star processing new file:
ed.WriteMessage("\nProcessing: " + Path.GetFileName(dwgfile));
...............
..............
end processing the file
ed.UpdateScreen();
but I see my screen updated just at the end of whole program... strangely enogh.
[CommandMethod("REPBLK")]
public static void RunDocCommand()
{
string pathDwgFiles = @"MAYPATH";
string newBlockFile = @"DWGFILEWITHNEWBLOCK";
string oldBlkName = "OLDBLOCKNAME";
string newBlkName = "NEWBLOCKNAME";
var workingDb = HostApplicationServices.WorkingDatabase;
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
try
{
ed.WriteMessage("\nSTART BLOCK REPLACE PROCESS\n");
string[] dwgFiles = Directory.GetFiles(pathDwgFiles, "*.dwg", SearchOption.AllDirectories);
foreach (var dwgfile in dwgFiles)
{
// Open target drawing as side database.
// It makes the processing a lot faster than opening
// drawing in Editor visibly.
// One can choose open the drawing in Editor, if desired.
ed.WriteMessage("\nProcessing: " + Path.GetFileName(dwgfile));
using (var db = OpenSideDatabase(dwgfile))
{
HostApplicationServices.WorkingDatabase = db;
if (!ReplaceBlocks(db, oldBlkName, newBlkName, newBlockFile))
ed.WriteMessage("\nOld Block NOT foud in: " + Path.GetFileName(dwgfile));
else
RemoveBlockFromDb(db, oldBlkName); //remove oldBlock
db.RetainOriginalThumbnailBitmap = true;
db.SaveAs(dwgfile, true, DwgVersion.Current, null);
}
ed.UpdateScreen();
}
}
catch (System.Exception ex)
{
CadApp.ShowAlertDialog("Error:\r\n" + ex.Message);
}
finally
{
ed.WriteMessage("\nEND BLOCK REPLACE PROCESS\n");
HostApplicationServices.WorkingDatabase = workingDb;
Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
}
}
/// <summary>
/// Open the DWG as Database
/// </summary>
/// <param name="dwgFileName">the dwg file name full path</param>
/// <returns>the Database</returns>
private static Database OpenSideDatabase(string dwgFileName)
{
var db = new Database(false, true);
db.ReadDwgFile(dwgFileName, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
return db;
}
private static bool ReplaceBlocks(Database db, string oldBlkName, string newBlkName, string newBlockFile)
{
// Find all "OldBlock" references and collect their attribute data
// If only blockreference on certain layout is targeted, this is where
// one can decide which layout to be excluded
var dwgAttData = CollectOldBlockAttributeData(db, oldBlkName);
if (dwgAttData.Count == 0)
{
//CadApp.ShowAlertDialog("No \"" + oldBlkName + "\" found!");
return false;
}
// Make sure "NewBlock" is defined in the drawing. If not, get it from
// a block drawing file (Database.Insert(); or the the block definition
// is in a drawing where many blocks are defined, use
// use Database.WblockCloneObjects() to bring this block definition
// into this drawing
var newBlkDefId = EnsureNewBlockDefinition(db, newBlkName, newBlockFile);
if (newBlkDefId.IsNull)
{
CadApp.ShowAlertDialog("NewBlock definition is not found in source drawing!");
return false;
}
// Replacing "OldBlock" references by
// 1. Insert "NewBlock" at the same position/same layer
// 2. Sych attribute values (because both old and new blocks have the same Attrs.
// 3. Erase "OldBlock" reference
ReplaceOldBlocks(db, newBlkDefId, dwgAttData, newBlkName);
return true;
}
#region find all "OldBlock" and their attribute information
private static DrawingAttributeData CollectOldBlockAttributeData(Database db, string oldBlkName)
{
var attData = new DrawingAttributeData();
using (var tran = db.TransactionManager.StartTransaction())
{
var dic = (DBDictionary)tran.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
foreach (DBDictionaryEntry entry in dic)
{
var layout = (Layout)tran.GetObject(entry.Value, OpenMode.ForRead);
var layoutAttData = CollectOldBlockAttributeDataOnLayout(oldBlkName, layout.BlockTableRecordId, tran);
if (layoutAttData.Count > 0)
{
attData.Add(layout.BlockTableRecordId, layoutAttData);
}
}
tran.Commit();
}
return attData;
}
private static List<BlockAttributeData> CollectOldBlockAttributeDataOnLayout(
string oldBlkName, ObjectId layoutBlockId, Transaction tran)
{
var layoutAttData = new List<BlockAttributeData>();
var layoutBlock = (BlockTableRecord)tran.GetObject(layoutBlockId, OpenMode.ForRead);
foreach (ObjectId id in layoutBlock)
{
if (id.ObjectClass.DxfName.ToUpper() == "INSERT")
{
var blk = (BlockReference)tran.GetObject(id, OpenMode.ForRead);
if (blk.Name.ToUpper().Contains(oldBlkName.ToUpper())) //I use contain for similar names in my list
{
var blkAttData = CollectOldBlockAttributes(blk, tran);
if (blkAttData.Attributes.Count > 0)
{
layoutAttData.Add(blkAttData);
}
}
}
}
return layoutAttData;
}
private static BlockAttributeData CollectOldBlockAttributes(BlockReference bref, Transaction tran)
{
//bref.Name is the layout name.
var blkAttData = new BlockAttributeData();
blkAttData.BlkRefId = bref.ObjectId;
blkAttData.Layer = bref.Layer;
foreach (ObjectId id in bref.AttributeCollection)
{
var att = (AttributeReference)tran.GetObject(id, OpenMode.ForRead);
if (att.IsConstant)
continue;
if (!blkAttData.Attributes.ContainsKey(att.Tag.ToUpper()))
{
blkAttData.Attributes.Add(att.Tag.ToUpper(), att.TextString);
}
}
return blkAttData;
}
#endregion
#region make sure "NewBlock" definition exists
private static ObjectId EnsureNewBlockDefinition(Database db, string newBlkName, string newBlockFile)
{
var blkId = ObjectId.Null;
using (var tran = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
if (bt.Has(newBlkName))
blkId = bt[newBlkName]; //verify if the current drawing has the new block
tran.Commit();
}
if (blkId.IsNull) //if the block isn't in, get it
{
blkId = InsertNewBlockDefinition(db, newBlkName, newBlockFile);
}
return blkId;
}
/// <summary>
/// Clones in db the block from the file
/// </summary>
/// <param name="db">destination database</param>
/// <param name="blkName">block name</param>
/// <param name="blkFile">block's source file full path</param>
/// <returns>the block's objId</returns>
private static ObjectId InsertNewBlockDefinition(Database db, string blkName, string blkFile)
{
var blkId = ObjectId.Null;
using (var blkDb = new Database(false, true))
{
blkDb.ReadDwgFile(blkFile, FileOpenMode.OpenForReadAndReadShare, false, null);
//this Works:
//need to crate collectionObject for wblockClone method:
ObjectIdCollection objColl = new ObjectIdCollection();
objColl.Add(GetBlkId(blkDb, blkName));
IdMapping map = new IdMapping();
//cloneObject
blkDb.WblockCloneObjects(objColl, db.BlockTableId, map, DuplicateRecordCloning.Replace, false);
blkId = GetBlkId(db, blkName); //keep the id of cloned block
//if I use deepclone, as is doesn't work
//blkDb.DeepCloneObjects(objColl, db.BlockTableId, map, false);
//if I use Insert, as is doesn't work:
//blkId = db.Insert(blkName, blkDb, false);
}
return blkId;
}
#endregion
#region do the replacing work
private static void ReplaceOldBlocks(Database db, ObjectId newBlkDefId,
DrawingAttributeData dwgAttData, string newBlkName)
{
using (var tran = db.TransactionManager.StartTransaction())
{
foreach (var item in dwgAttData)
{
//item.key is an objectId
var layoutBlock = (BlockTableRecord)tran.GetObject(item.Key, OpenMode.ForWrite);
var newBlock = (BlockTableRecord)tran.GetObject(newBlkDefId, OpenMode.ForRead);
//foreach layout
foreach (var blkData in item.Value)
{
//get the position
var oldBlk = (BlockReference)tran.GetObject(blkData.BlkRefId, OpenMode.ForRead);
var position = oldBlk.Position;
CreateNewBlockReference(db, layoutBlock, newBlock, position, blkData.Layer,
blkData.Attributes, tran);
//erase block doesn't work - i will erase later see: RemoveBlockFromDb
//oldBlk.UpgradeOpen();
//oldBlk.Erase();
//oldBlk.Dispose();
}
}
tran.Commit();
}
}
private static void CreateNewBlockReference(Database db, BlockTableRecord layoutBlock,
BlockTableRecord newBlock, Point3d position,
string layer, Dictionary<string, string> attData,
Transaction tran)
{
var bref = new BlockReference(position, newBlock.ObjectId);
bref.SetDatabaseDefaults(db);
bref.Layer = layer;
var brefId = layoutBlock.AppendEntity(bref);
tran.AddNewlyCreatedDBObject(bref, true);
SetAttributes(brefId, newBlock, attData, tran);
}
private static void SetAttributes(ObjectId brefId, BlockTableRecord newBlock,
Dictionary<string, string> attData, Transaction tran)
{
var bref = (BlockReference)tran.GetObject(brefId, OpenMode.ForWrite);
foreach (ObjectId id in newBlock)
{
//FROM .NET SAMPLE GUIDE
DBObject dbObj = (DBObject)tran.GetObject(id, OpenMode.ForRead);
if (dbObj is AttributeDefinition)
{
AttributeDefinition attDef = dbObj as AttributeDefinition;
if (!attDef.Constant)
{
using (AttributeReference attRef = new AttributeReference())
{
attRef.SetAttributeFromBlock(attDef, bref.BlockTransform);
if (attDef.Tag.Contains("THE TAG I WANT TO REPLACE"))
{
//replace value gets from the old block
attRef.TextString = attData["THE TAG I WANT TO REPLACE"];
}
else
{
//Do nothing and leave value of new block
}
bref.AttributeCollection.AppendAttribute(attRef);
tran.AddNewlyCreatedDBObject(attRef, true);
}
}
}
//OLD VERSION
//var attDef = tran.GetObject(id, OpenMode.ForRead) as AttributeDefinition;
//if (attDef != null)
//{
// var att = new AttributeReference();
// att.SetAttributeFromBlock(attDef, bref.BlockTransform);
// if (!attDef.Constant && attData.ContainsKey("OGGETTO"))
// {
// if (attData.ContainsKey(att.Tag.ToUpper()))
// att.TextString = attData[att.Tag.ToUpper()];
// }
// bref.AttributeCollection.AppendAttribute(att);
// tran.AddNewlyCreatedDBObject(att, true);
//}
}
}
#endregion
#region MyAdd
/// <summary>
/// Gets the block's ObjID from the database
/// </summary>
/// <param name="db">the Database</param>
/// <param name="blkName">the block name</param>
/// <returns>the objID</returns>
public static ObjectId GetBlkId(Database db, string blkName)
{
ObjectId blkId = ObjectId.Null;
if (db == null)
return ObjectId.Null;
if (string.IsNullOrWhiteSpace(blkName))
return ObjectId.Null;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
if (bt.Has(blkName))
blkId = bt[blkName];
else //looking for similar name...
{
foreach (ObjectId item in bt)
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(item, OpenMode.ForRead);
if (btr.Name.ToUpper().Contains(blkName.ToUpper()))
{
blkId = item;
break;
}
}
}
tr.Commit();
}
return blkId;
}
/// <summary>
/// Get the Value of Attribute from block
/// </summary>
/// <param name="btrId">The object database</param>
/// <param name="blockName">the block name</param>
/// <param name="attbName"> the attribute name</param>
/// <returns>the attribute value</returns>
private static string GetAttribValueFromBlock(ObjectId btrId, string blockName, string attbName)
{
Database db = btrId.Database;
string txtValue = string.Empty;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
// Test each entity in the container...
foreach (ObjectId entId in btr)
{
Entity ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;
if (ent != null)
{
BlockReference br = ent as BlockReference;
if (br != null)
{
BlockTableRecord bd = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForRead);
// ... to see whether it's a block with
// the name we're after
if (bd.Name.ToUpper() == blockName)
{
// Check each of the attributes...
foreach (ObjectId arId in br.AttributeCollection)
{
DBObject obj = tr.GetObject(arId, OpenMode.ForRead);
AttributeReference ar = obj as AttributeReference;
if (ar != null)
{
// ... to see whether it has
// the tag we're
if (ar.Tag.ToUpper() == attbName)
txtValue = ar.TextString;
}
}
}
// Recurse for nested blocks
//doesn't needed for my block
//changedCount += UpdateAttributesInBlock(br.BlockTableRecord,blockName,attbName,attbValue);
}
}
}
tr.Commit();
}
return txtValue;
}
private static bool SetAttributeInBlock(ObjectId btrId, string blockName, string attbName, string attbValue)
{
Database db = btrId.Database;
bool gotcha = false;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
// Test each entity in the container...
foreach (ObjectId entId in btr)
{
Entity ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;
if (ent != null)
{
BlockReference br = ent as BlockReference;
if (br != null)
{
BlockTableRecord bd = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForRead);
// ... to see whether it's a block with
// the name we're after
if (bd.Name.ToUpper() == blockName)
{
// Check each of the attributes...
foreach (ObjectId arId in br.AttributeCollection)
{
DBObject obj = tr.GetObject(arId, OpenMode.ForRead);
AttributeReference ar = obj as AttributeReference;
if (ar != null)
{
// ... to see whether it has
// the tag we're
if (ar.Tag.ToUpper() == attbName)
{
// If so, update the value
// and increment the counter
ar.UpgradeOpen();
ar.TextString = attbValue;
ar.DowngradeOpen();
gotcha = true;
}
}
}
}
// Recurse for nested blocks
//doesn't needed for my block
//changedCount += UpdateAttributesInBlock(br.BlockTableRecord,blockName,attbName,attbValue);
}
}
}
tr.Commit();
}
return gotcha;
}
public static void RemoveBlockFromDb(Database db, string blkName)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ObjectId blkId = GetBlkId(db, blkName);
if (!EraseBlkRefs(blkId))
ed.WriteMessage(string.Format("\n failed to erase blockreferences for: {0}", blkId.ToString()));
if (!EraseBlk(blkId))
ed.WriteMessage(string.Format("\n Failed to Erase Block: {0}", blkId.ToString()));
else
ed.WriteMessage("\nErased" + blkId.ToString());
}
public static bool EraseBlkRefs(ObjectId blkId)
{
bool blkRefsErased = false;
if (blkId.IsNull)
return false;
Database db = blkId.Database;
if (db == null)
return false;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord blk = (BlockTableRecord)tr.GetObject(blkId, OpenMode.ForRead);
var blkRefs = blk.GetBlockReferenceIds(true, true);
if (blkRefs != null && blkRefs.Count > 0)
{
foreach (ObjectId blkRefId in blkRefs)
{
BlockReference blkRef = (BlockReference)tr.GetObject(blkRefId, OpenMode.ForWrite);
blkRef.Erase();
}
blkRefsErased = true;
}
tr.Commit();
}
return blkRefsErased;
}
public static bool EraseBlk(ObjectId blkId)
{
bool blkIsErased = false;
if (blkId.IsNull)
return false;
Database db = blkId.Database;
if (db == null)
return false;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord blk = (BlockTableRecord)tr.GetObject(blkId, OpenMode.ForRead);
var blkRefs = blk.GetBlockReferenceIds(true, true);
if (blkRefs == null || blkRefs.Count == 0)
{
blk.UpgradeOpen();
blk.Erase();
blkIsErased = true;
}
tr.Commit();
}
return blkIsErased;
}
#endregion