I would like to ask question how to mimic Block Definition Save As function.
So far what I have found in this forum and TheSwamp the best way is to use DeepClone of BlockTableRecord but the objects in original BlockTableRecord need to be DeepCloneObjectes into new block. Otherwise new block will have all objects with ObjectId from old BlockTabkeRecord
Is that right way how to do it or is there some simpler solution.
Thanks,
Richard
Can't you just use Database.WBlockCloneObjects()?
Database.WBlockCloneObjects can be used to copy objects between two databases.
In my case I need to make copy of BlockTableRecord in the same database.
Any another idea?
Thanks.
I thought you need to deepclone main block first,
then deepclone embedded block in the newly created
copy of main instance
, just an idea sorry
~'J'~
Thanks for hint but unfortunately my block consists only single entities (line, text).
But let me explain the problem is simple steps.
1)You have BlockTableRecord with one Line only
2)DeepClone this BlockTableRecord
3)Get ObjectId of Line in newly created BlockTable and change the length
4) As result the line will change the length in both old and new BlockTable.
As it was mention somewhere in this forum DeepClone of BlockTableRecord apparently does not translate ObjectIds of cloned Entities inside of new BlockTableRecord.
Hi Richard,
Here is a sample code how to clone block
with embeddingan another block to the same copy
Try to rewrite it to your needs, this one is from my oldies,
so I haven't have tested it enough, sorry
[CommandMethod("CopyXBref")] public void testCopyBlockReference() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); PromptEntityOptions peo = new PromptEntityOptions("\nSelect main block instance to copy: "); peo.SetRejectMessage("\nMust be a type of the BlockReference!"); peo.AddAllowedClass(typeof(BlockReference), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; BlockReference bref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForRead); BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead); ed.WriteMessage("\nBlock Selected with name: {0}", btr.Name); PromptStringOptions pso = new PromptStringOptions("\nEnter new block name: "); pso.AllowSpaces = true; PromptResult sres = ed.GetString(pso); if (sres.Status != PromptStatus.OK) return; string newname = sres.StringResult; if (bt.Has(newname)) { ed.WriteMessage("\nBlock with name: {0} already exist, try again", newname); return; } BlockTableRecord newbtr = new BlockTableRecord(); bt.UpgradeOpen(); newbtr = (BlockTableRecord)btr.DeepClone(bt, new IdMapping(), true); newbtr.Name = newname; bt.Add(newbtr); //----------------------------------------------------------------// peo.Message = "\nSelect block instance to be embedded to main block: "; per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; BlockReference xbref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForRead); BlockTableRecord xbtr = (BlockTableRecord)tr.GetObject(xbref.BlockTableRecord, OpenMode.ForRead); PromptPointOptions ppo = new PromptPointOptions("\nSpecify a new location of embedded block: "); PromptPointResult ppr = ed.GetPoint(ppo); if (ppr.Status != PromptStatus.OK) return; Point3d pt = ppr.Value; foreach (ObjectId id in newbtr) { Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent == null)continue; ent.UpgradeOpen(); ent.ColorIndex = 0; } foreach (ObjectId eid in xbtr) { Entity xent = tr.GetObject(eid, OpenMode.ForRead) as Entity; if (xent == null)continue; xent.UpgradeOpen(); xent.ColorIndex = 0; DBObject obj=((DBObject)xent).DeepClone(newbtr, new IdMapping(), true); ((Entity)obj).TransformBy(Matrix3d.Displacement(pt - bref.Position)); } tr.AddNewlyCreatedDBObject(newbtr, true); tr.Commit(); } }
~'J'~
Richard,
You can change the subentity properties easily,
see example, just change void name, command name
and default length of line to your needs:
[CommandMethod("reline")] static public void SwapSubEntity() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; Transaction tr = doc.TransactionManager.StartTransaction(); using (tr) { PromptNestedEntityOptions pno = new PromptNestedEntityOptions("\nSelect Line >>"); pno.AllowNone = false; PromptNestedEntityResult rs = ed.GetNestedEntity(pno); if (rs.Status != PromptStatus.OK) return; Entity selent = (Entity)tr.GetObject(rs.ObjectId, OpenMode.ForWrite); BlockTableRecord btrec = tr.GetObject(selent.OwnerId, OpenMode.ForWrite) as BlockTableRecord; Line ln = selent as Line; if (ln != null) { if (!ln.IsWriteEnabled) ln.UpgradeOpen(); double leg = ln.Length; PromptDoubleOptions pdo = new PromptDoubleOptions("\nSpecify new line length: "); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = true; pdo.DefaultValue = 12; PromptDoubleResult res = ed.GetDouble(pdo); if (res.Status != PromptStatus.OK && res.Status != PromptStatus.Keyword) return; double newleg = res.Value; ln.TransformBy(Matrix3d.Scaling(newleg / leg, ln.StartPoint)); btrec.DowngradeOpen(); } doc.TransactionManager.QueueForGraphicsFlush(); doc.TransactionManager.FlushGraphics(); doc.Editor.UpdateScreen(); tr.Commit(); ed.Regen(); } }
Then you can clone this block as I've wrote above,
let me know if you need more help
~'J'~
Hi Hallex,
I think I found what problem is in my case.
Probably I should mention on the beginning that I need to also change BlockReference.BlockTableRecord once a new BlockTable is created.
I modified your first example and at the end I added line to change BlockReference.
Could you please try create block with some lines and insert at least two blocks into drawing and try run modified CopyXBref command on one block.
When I run REGEN command lines change colour in both (old and new) blocks
Thanks for your examples.
Richard.
[CommandMethod("CopyXBref")]
public void testCopyBlockReference()
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
PromptEntityOptions peo = new PromptEntityOptions("\nSelect main block instance to copy: ");
peo.SetRejectMessage("\nMust be a type of the BlockReference!");
peo.AddAllowedClass(typeof(BlockReference), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
BlockReference bref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForWrite);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead);
ed.WriteMessage("\nBlock Selected with name: {0}", btr.Name);
PromptStringOptions pso = new PromptStringOptions("\nEnter new block name: ");
pso.AllowSpaces = true;
PromptResult sres = ed.GetString(pso);
if (sres.Status != PromptStatus.OK) return;
string newname = sres.StringResult;
if (bt.Has(newname))
{
ed.WriteMessage("\nBlock with name: {0} already exist, try again", newname);
return;
}
BlockTableRecord newbtr = new BlockTableRecord();
bt.UpgradeOpen();
newbtr = (BlockTableRecord)btr.DeepClone(bt, new IdMapping(), true);
newbtr.Name = newname;
bt.Add(newbtr);
//----------------------------------------------------------------//
foreach (ObjectId id in newbtr)
{
Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
if (ent == null) continue;
ent.UpgradeOpen();
ent.ColorIndex = 0;
}
tr.AddNewlyCreatedDBObject(newbtr, true);
//Change BlockReference
bref.BlockTableRecord = newbtr.Id;
tr.Commit();
}
}
In this case you have to change properties
of all subentities inside the both BlockTableRecords,
try this one
[CommandMethod("CopyXBref")] public void testCopyBlockReference() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); PromptEntityOptions peo = new PromptEntityOptions("\nSelect main block instance to copy: "); peo.SetRejectMessage("\nMust be a type of the BlockReference!"); peo.AddAllowedClass(typeof(BlockReference), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; BlockReference bref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForWrite); BlockTableRecord bdef = (BlockTableRecord)tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead); foreach (ObjectId id in bdef) { Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent == null) continue; ent.UpgradeOpen(); ent.ColorIndex = 1; } // BlockTableRecord cursp = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); ed.WriteMessage("\nBlock Selected with name: {0}", bdef.Name); PromptStringOptions pso = new PromptStringOptions("\nEnter new block name: "); pso.AllowSpaces = true; PromptResult sres = ed.GetString(pso); if (sres.Status != PromptStatus.OK) return; string newname = sres.StringResult; if (bt.Has(newname)) { ed.WriteMessage("\nBlock with name: {0} already exist, try again", newname); return; } BlockTableRecord newbtr = new BlockTableRecord(); bt.UpgradeOpen(); newbtr = (BlockTableRecord)bdef.DeepClone(bt, new IdMapping(), true); newbtr.Name = newname; bt.Add(newbtr); //----------------------------------------------------------------// foreach (ObjectId id in newbtr) { Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent == null) continue; ent.UpgradeOpen(); ent.ColorIndex = 1; } tr.AddNewlyCreatedDBObject(newbtr, true); //Change BlockReference bref.BlockTableRecord = newbtr.Id; ed.Regen(); tr.Commit(); } }
~'J'~
I think you misunderstood my last message.
I don’t want to change original block at all. What I was trying to show you in my example is that changing of colour of entities in new block will change colour of entities in original block (that is what I do not want).
Thanks,
Richard
Richard, try first to create 2 similar blocks
with different names manually
and set color property for all subentities
in the first block to byblock and for the second one
to bylayer, then see what happens
~'J'~
I have tried to create manually two blocks as you advised and inserted them into drawing (two per each one).
Then I run my version of CopyXBref command. Unfortunately original block had change colour as well.
Thank you for your patience
Richard
No problems,
Now you try this code on your manually created block<
but create layer before, see comments at the end of code
[CommandMethod("CopyXB")] public void CopyBlockDef() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); PromptEntityOptions peo = new PromptEntityOptions("\nSelect parent block instance to copy: "); peo.SetRejectMessage("\nMust be a type of the BlockReference!"); peo.AddAllowedClass(typeof(BlockReference), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; BlockReference bref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForWrite); BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead); ed.WriteMessage("\nBlock Selected with name: {0}", btr.Name); PromptStringOptions pso = new PromptStringOptions("\nEnter new block name: "); pso.AllowSpaces = true; PromptResult sres = ed.GetString(pso); if (sres.Status != PromptStatus.OK) return; string newname = sres.StringResult; if (bt.Has(newname)) { ed.WriteMessage("\nBlock with name: {0} already exist, try again", newname); return; } BlockTableRecord newbtr = new BlockTableRecord(); bt.UpgradeOpen(); newbtr = (BlockTableRecord)btr.DeepClone(bt, new IdMapping(), true); newbtr.Name = newname; bt.Add(newbtr); ed.Regen(); //----------------------------------------------------------------// foreach (ObjectId id in newbtr) { Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent == null) continue; ent.UpgradeOpen(); ent.ColorIndex = 256; } tr.AddNewlyCreatedDBObject(newbtr, true); //Change BlockReference bref.BlockTableRecord = newbtr.ObjectId; // change layer, "RedColoredLayer" must be created before bref.Layer = "RedColoredLayer"; tr.Commit(); } }
~'J'~
Did not work either.
I set subentities in original block to color 3 but after running this command subentities were assigned to color ByLayer (ColorIndex = 256) in new and original block.
As I said at the beginning of the thread, ObjectIds of subentities in original and new blocks are the same and that is an issue.
I decided to create new empty Block and use DeepCloneObjects instead. Example below.
Hallex, Thank you for your help and very good examples.
Richard
[CommandMethod("CopyXC")]
public void testCopyBlockReference()
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
PromptEntityOptions peo = new PromptEntityOptions("\nSelect main block instance to copy: ");
peo.SetRejectMessage("\nMust be a type of the BlockReference!");
peo.AddAllowedClass(typeof(BlockReference), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
BlockReference bref = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForWrite);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead);
ed.WriteMessage("\nBlock Selected with name: {0}", btr.Name);
PromptStringOptions pso = new PromptStringOptions("\nEnter new block name: ");
pso.AllowSpaces = true;
PromptResult sres = ed.GetString(pso);
if (sres.Status != PromptStatus.OK) return;
string newname = sres.StringResult;
if (bt.Has(newname))
{
ed.WriteMessage("\nBlock with name: {0} already exist, try again", newname);
return;
}
ObjectIdCollection ids = new ObjectIdCollection();
foreach (ObjectId id in btr)
{
ids.Add(id);
}
BlockTableRecord newbtr = new BlockTableRecord();
bt.UpgradeOpen();
newbtr.Name = newname;
ObjectId newBtrId = bt.Add(newbtr);
tr.AddNewlyCreatedDBObject(newbtr, true);
//----------------------------------------------------------------//
IdMapping idMap = new IdMapping();
db.DeepCloneObjects(ids, newBtrId, idMap, true);
foreach (ObjectId id in newbtr)
{
Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
if (ent == null) continue;
ent.UpgradeOpen();
ent.ColorIndex = 1;
}
//Change BlockReference
bref.BlockTableRecord = newbtr.Id;
tr.Commit();
}
}
Can't find what you're looking for? Ask the community or share your knowledge.