... If that block is copied, it needs to be renamed because two instances of the same block will cause data calculations to be incorrect.
...
Consider this scenario:
Insert block "a" on a layout named "Current" then rename block "a" to "a-Current-n", where n is an index number;
Copy "Current" layout and rename it to "Future";
Rename all block references on "Future" layout to "a-Future-n" where n is the index number;
"a" will remain the same, n will remain the same, the only thing that changes is "Current" changes to "Future".
...
/yes so you only need 1 sub replaceblockrefs(). And you are using the paperspace block belonging to the layout to cycle thru the blockfrefs. Using a dictionary to copy the atts.No code as of yet .. coming to terms with the best way to manage doing it. Only then will I write some code, but it is getting close!
Do the blocks have to be named differently? Could you use Xdata instead? If the objects are on different tabs, it's very easy to grab all objects per tab.
@KeithBrown
Hi Keith,
Without knowledge of the ultimate goal I'm loath to suggest alternative structures.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
[assembly: CommandClass(typeof(ChangeBlockDefinition.Commands))]
namespace ChangeBlockDefinition
{
public class Commands
{
[CommandMethod("DoWork")]
public static void RunMyCommand()
{
var doc = CadApp.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
string originalBlock = "Original";
try
{
//Copy content from "Layout1" to new layouts
var layoutNames = CreateLayouts();
//Create new block definitions corresponding to each layout
var newBlockDefIds =
DefineBlockForEachLayout(doc, originalBlock, layoutNames);
//Update BlockReference on each layout to be BlockRefernce of
//newly created block definition
UpdateBlockReferenceOnLayouts(doc, newBlockDefIds);
}
catch (System.Exception ex)
{
ed.WriteMessage("\nError\n{0}", ex.Message);
ed.WriteMessage("\n*Cancel*");
}
finally
{
Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
}
}
// Assume the drawing has one layout ("Layout1" with one block reference, named as
// 'Original" inserted at (0.0, 0.0, 0.0), which has a few attributes.
// Using LayoutManager.CopyLayout() to create a few more layouts, named as
// "Layout2", "Layout3"...
private static IEnumerable<string> CreateLayouts()
{
var layoutNames = new List<string>();
string sourceLayout = "Layout1";
layoutNames.Add(sourceLayout);
for (int i=2; i<=5; i++)
{
string newLayout = "Layout" + i;
layoutNames.Add(newLayout);
LayoutManager.Current.CopyLayout(sourceLayout, newLayout);
}
LayoutManager.Current.CurrentLayout = sourceLayout;
return layoutNames;
}
// Create new block definitions, which is the same as the original block defnition
// except for it name
private static Dictionary<string, ObjectId> DefineBlockForEachLayout(
Document dwg, string sourceBlkName, IEnumerable<string> layoutNames)
{
var dic = new Dictionary<string, ObjectId>();
using (var tran = dwg.TransactionManager.StartTransaction())
{
var blkTable = (BlockTable)tran.GetObject(dwg.Database.BlockTableId, OpenMode.ForRead);
if (blkTable.Has(sourceBlkName))
{
var sourceBlkId = blkTable[sourceBlkName];
foreach (var layout in layoutNames)
{
var blkName = sourceBlkName + "_" + layout;
var newBlkId = CreateNewBlockDefinition(dwg.Database, sourceBlkId, blkName);
dic.Add(layout, newBlkId);
}
}
tran.Commit();
}
return dic;
}
private static ObjectId CreateNewBlockDefinition(
Database sourceDB, ObjectId sourceBlkId, string blkName)
{
ObjectId blkId = ObjectId.Null;
using (var blkDb = sourceDB.Wblock(sourceBlkId))
{
blkId = sourceDB.Insert(blkName, blkDb, true);
}
return blkId;
}
private static void UpdateBlockReferenceOnLayouts(
Document dwg, Dictionary<string, ObjectId> layoutBlockDefIds)
{
bool regen = false;
using (var tran = dwg.TransactionManager.StartTransaction())
{
foreach (var item in layoutBlockDefIds)
{
var brefIds = GetBlockReferencesOnLayout(dwg.Editor, item.Key);
if (brefIds!=null)
{
foreach (var blkId in brefIds)
{
var blk = (BlockReference)tran.GetObject(blkId, OpenMode.ForRead);
if (blk.Name.ToUpper()=="ORIGINAL")
{
blk.UpgradeOpen();
//Set the BlockReference to be a reference to a new block definition
blk.BlockTableRecord = item.Value;
//Update attributes in the block reference here, if necessary
// ... ...
if (!regen) regen = true;
}
}
}
}
tran.Commit();
}
if (regen) dwg.Editor.Regen();
}
private static ObjectId[] GetBlockReferencesOnLayout(Editor ed, string layout)
{
var vals = new TypedValue[]
{
new TypedValue((int)DxfCode.Start,"INSERT"),
new TypedValue((int)DxfCode.LayoutName, layout)
};
var res = ed.SelectAll(new SelectionFilter(vals));
if (res.Status== PromptStatus.OK)
{
return res.Value.GetObjectIds();
}
else
{
return null;
}
}
}
}
Well, your code looks simpler than the steps you described. Obviously the code only does part of the steps in your description.
I'm starting to develop a direction of how I think this would be best handled. How does this sound for a beginning ...
1) Copy the layout and existing block references to a new layout;
2) Get a selection set of all block references;
3) For each block reference
a) Get the base block type, there will ultimately be 8 to 10, from the block definition name (for argument sake, we'll call it "a");
b) Store a copy of the attribute values in a predefined data class (this is already in use in another part of the program);
c) Create a new block definition using the predefined block "a" and rename it to "a-layoutname-n";
d) Create a block reference for the new block definition at the insertion point of the original block and with the same scale and rotation;
e) Populate the attributes of "a-layoutname-n" with the stored attribute values;
f) Fire the event handler (already defined) to do the magic with the new block reference;
g) Wash, rinse and repeat;
4) Delete the original block definition;
I'm thinking this is probably the shortest way to get from point A to point B ....