I'm trying to add a block with an attribute to a drawing. The command works if there is only one drawing open, but when there are other open drawings then the block doesn't appear. Strangely if I select the block in the block editor and then close the block editor the block suddenly appears. Do you know what is causing the block to not appear when other drawings are open?
I included the code below. "s1" works but "s2" has the strange behavior of the block not being visible.
Thanks for any help,
Casey Joyce
[CommandMethod("s1")]
public void AddBlockWithAttributeToDestination_WithNoOtherDrawingsOpen() {
var attributeValue = "1234";
var destination = Application.DocumentManager.MdiActiveDocument;
using (var transaction = destination.TransactionManager.StartTransaction()) {
addBlock(transaction, destination.Database, attributeValue);
transaction.Commit();
}
}
[CommandMethod("s2")]
public void AddBlockWithAttributeToDestination_WhileSourceDrawingIsOpen() {
// pretend user selects info from opened source drawing
var attributeValue = "1234";
// pretend user selects destination drawing from popup list
var destinationDrawing = @"c:\destination.dwg";
// add block to destination drawing
var destination = Application.DocumentManager.Open(destinationDrawing, false);
using (var transaction = destination.TransactionManager.StartTransaction()) {
using (var key = destination.LockDocument()) {
addBlock(transaction, destination.Database, attributeValue);
}
transaction.Commit();
}
}
void addBlock(Transaction transaction, Database database, string attributeValue) {
// add block
var block = new BlockTableRecord() { Name = "SampleBlock" };
var table = transaction.GetObject(database.BlockTableId, OpenMode.ForWrite) as BlockTable;
var blockId = table.Add(block);
transaction.AddNewlyCreatedDBObject(block, true);
// add block reference
var blockReference = new BlockReference(Point3d.Origin, block.ObjectId);
var modelSpace = transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
modelSpace.AppendEntity(blockReference);
transaction.AddNewlyCreatedDBObject(blockReference, true);
// set attribute definition
var definition = new AttributeDefinition(
new Point3d(1, 1, 0),
attributeValue,
"SampleAttribute",
"",
database.Textstyle);
block.AppendEntity(definition);
transaction.AddNewlyCreatedDBObject(definition, true);
// add attribute reference
var reference = new AttributeReference();
reference.SetAttributeFromBlock(definition, blockReference.BlockTransform);
blockReference.AttributeCollection.AppendAttribute(reference);
transaction.AddNewlyCreatedDBObject(reference, true);
}
Solved! Go to Solution.
Thanks for your response GTVic. I am using LockDocument in the s2 command, and that's the one that wasn't working.
I think I am going to be able to get s2 to work by adding CommandFlags.Session. So far it looks like its working.
[CommandMethod("s2", CommandFlags.Session)]
Sorry, didn't notice that, maybe because it was inside the transaction. I think that is a problem, the document should be locked before starting the transaction. That is the way it is documented in the Developer's Guide
CommandFlags.Session might mask the problem. The effect of "Session" is not clear but I take it that nothing else can happen in the application while this command is running and then perhaps there is no need for a lock and the lock being out of order doesn't cause a problem.
I moved the LockDocument outside the transaction as you and the Developer Guide suggested, thanks. When I keep the CommandFlags.Session this seems to work fine. So unless I find a better way I guess I'll stick with this.
Just FYI:
Even with the lock on the outside of the transaction, the CommandFlags.Session is still required for me to get "s2" to work.
I tried removing the LockDocument all together to see if CommandFlags.Session would take care of it, but it throws an exception in that case.
Maybe you need to lock the source drawing, if I'm understanding your code correctly.
This is my subroutine to copy block definitions from a template DWG to the current drawing using the clone method. I use it for updating block definitions or bringing in missing blocks.
Private Sub LoadBlocks() Dim dm As DocumentCollection = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager Dim ed As Editor = dm.MdiActiveDocument.Editor Dim destDb As Database = dm.MdiActiveDocument.Database Dim sourceDb As Database = New Database(False, True) Dim mapping As IdMapping = New IdMapping() Dim blockIds As ObjectIdCollection = New ObjectIdCollection() Dim s As String Dim DisplayError As Boolean = False Try Using objLock As DocumentLock = AcadDoc.LockDocument() Try sourceDb.ReadDwgFile(Path.Combine(Path.Combine(EngToolsPath.FullName, "Source"), cboSource.Text), System.IO.FileShare.Read, True, "") Try Using Trans As Transaction = sourceDb.TransactionManager.StartTransaction() Try Dim bt As BlockTable = Trans.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, False) Dim btr As BlockTableRecord For Each btrId As ObjectId In bt btr = Trans.GetObject(btrId, OpenMode.ForRead, False) If lstItems.SelectedItems.Contains(btr.Name) Then blockIds.Add(btrId) End If btr.Dispose() Next Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(vbLf & "Error scanning for block definitions in source drawing: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(vbLf & "Error reading from source drawing: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try If Not DisplayError Then Try Using Trans As Transaction = destDb.TransactionManager.StartTransaction() Try sourceDb.WblockCloneObjects(blockIds, destDb.BlockTableId, mapping, DuplicateRecordCloning.Replace, False) Trans.Commit() Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.AcadDocument.Regen(1) Catch ex As Autodesk.AutoCAD.Runtime.Exception Trans.Abort() ed.WriteMessage(vbLf & "Error transferring blocks to current drawing: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(vbLf & "Error transferring blocks to current drawing: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try End If sourceDb.Dispose() Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(vbLf & "Error reading from source drawing: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(vbLf & "Error locking current drawing for update: " & ex.Message & vbLf) ed.UpdateScreen() DisplayError = True End Try lblError.Visible = DisplayError End Sub
BTW (Just checking) Do you actually want to open the document in the editor (i.e. does the user need to see and interact with it). If not, then it is a lot easier (and faster) to open a DWG file in a side database (using Database.ReadDwgFile) to modify it programmatically.
If you do want them to interact with it have you tried making it the active document first then add Block and BlockReference?
Thanks for everybody's responses. I do need both the source and destination drawing open for the user to interact with. Using the CommandFlags.Session and locking the drawings is working for my situation (at least so far).
Can't find what you're looking for? Ask the community or share your knowledge.