To erase a layout you can use the current LayoutManager's DeleteLayout() method. It will safely delete a layout including all contents.
Best of all, if you use this to erase the last remaining paper space layout, a new default paper space layout will automatically be generated by AutoCAD.
Note: If you try and delete the Model layout an exception will be thrown.
Here is a C# sample to erase all paper space layouts...
[CommandMethod("EraseAllLayouts")]
public static void EraseAllLayouts() {
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction()) {
// ACAD_LAYOUT dictionary.
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
// Iterate dictionary entries.
foreach (DBDictionaryEntry de in layoutDict) {
string layoutName = de.Key;
if (layoutName != "Model") {
LayoutManager.Current.DeleteLayout(layoutName); // Delete layout.
}
}
}
ed.Regen(); // Updates AutoCAD GUI to relect changes.
}
Hope this helps.
Art
Be careful with dictionary entries. I got caught doing the same thing.
You are using Dim deItem As DictionaryEntry to iterate through a DBDicitionary. You should use Dim deItem As DBDictionaryEntry.
I got diffrerent results when I made that change.
This is a great example, thanks. I am finding that the drawing regens throughout the process with "Regenerating model - caching viewports". Regenauto doesn't affect. Does anyone have a way to prevent the screen flashing etc? Also, sometimes the new default layout is the next number, ie "Layout 10". I haven't been able to resolve these small niggles but am really appreciative of the sample. Regards, Dale
Update: I had already removed the ed.Regen() line. Dale
Hi Dale,
The command line output can be temporarily suppressed by setting the NOMUTT system variable. A word of caution though, this can get a little complicated. You need to watch out for happens if the user enters UNDO. You don't want to accidentally leave the user with NOMUTT set to 1 (i.e. if exception is thrown). Refer to the following links for details on this:
http://through-the-interface.typepad.com/through_the_interface/2008/09/no-muttering-at.html
http://through-the-interface.typepad.com/through_the_interface/2008/09/more-quiet-comm.html
I think the flashing is caused because this is what happens if you were to do this manually in the AutoCAD UI, it just happens fast. You could set the current layout to model space first, i.e.:
LayoutManager.Current.CurrentLayout = "Model";
See what you mean by it being named "Layout10". Seems to happens when you are replacing a Layout1. Only thing I can think of is to manually rename it to whatever you want using the RenameLayout() method, i.e.:
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
if (layoutDict.Count == 1)
{
foreach (DBDictionaryEntry de in layoutDict)
{
string layoutName = de.Key;
if (layoutName != "Model")
{
LayoutManager.Current.RenameLayout(layoutName, "NewLayoutName");
}
}
}
Hope this helps,
Art
Thank you ArtVegas, discussion groups are really valuable.
I had this code to erase a layout:
Private Function DeleteLayout(ByVal LayoutNameToDelete As String) As Boolean
Dim bReturnValue As Boolean = True
Dim oLayoutIDToRename As ObjectId = Nothing
Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
Dim Db As Database = ed.Document.Database()
Dim acDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
oLayoutIDToRename = GetLayoutByName(LayoutNameToDelete)
Try
Using doclock As DocumentLock = acDoc.LockDocument
Using tm1 As Transaction = Db.TransactionManager.StartTransaction()
Dim oLayoutToRename As Layout = tm1.GetObject(oLayoutIDToRename, OpenMode.ForWrite)
oLayoutToRename.Erase()
tm1.Commit()
oLayoutToRename = Nothing
End Using
End Using
'Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.Regen()
Catch ex As Exception
ed.WriteMessage("\nImpossible de supprimer la présentation " & LayoutNameToDelete & ": " & ex.Message)
bReturnValue = False
End Try
Return bReturnValue
End Function
Here the function GetLayoutByName:
Public Function GetLayoutByName(ByVal layoutName As String) As ObjectId
Dim oReturnValue As ObjectId = Nothing
Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
Dim CurrentDb As Database = ed.Document.Database()
Try
''Create a variable to store the list oflayout identifiers
Dim tm As Autodesk.AutoCAD.DatabaseServices.TransactionManager = CurrentDb.TransactionManager
Using myT As Transaction = tm.StartTransaction
''Open the layout dictionary
Dim layoutDic As DBDictionary = myT.GetObject(CurrentDb.LayoutDictionaryId, OpenMode.ForRead, False)
'' Check each layout in the layout table
Dim Entry As DBDictionaryEntry = Nothing
For Each Entry In layoutDic
Dim layoutId As ObjectId = Entry.Value
Dim objLayout As Layout = myT.GetObject(layoutId, OpenMode.ForRead, False)
If objLayout.LayoutName.ToUpper = layoutName.ToUpper Then
oReturnValue = objLayout.ObjectId
End If
Next
myT.Commit()
End Using
Catch ex As Autodesk.AutoCAD.Runtime.Exception
oReturnValue = Nothing
End Try
Return oReturnValue
End Function
The layout is deleted but AutoCAD become unstable. When I close AutoCAD, it freeze. I don't know why. Now I replace my function by yours (converted in VB .Net and 3 modifications) and it works perfectly.
Dim bReturnValue As Boolean = True
Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
Dim Db As Database = ed.Document.Database()
Dim acDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
Try
Using doclock As DocumentLock = acDoc.LockDocument
Using tr As Transaction = Db.TransactionManager.StartTransaction
'ACAD_LAYOUT dictionary.
Dim layoutDict As DBDictionary = tr.GetObject(Db.LayoutDictionaryId, OpenMode.ForWrite)
'Iterate dictionary entries.
For Each de As DBDictionaryEntry In layoutDict
Dim layoutName As String = de.Key
If layoutName.ToUpper = LayoutNameToDelete.ToUpper Then
LayoutManager.Current.DeleteLayout(LayoutNameToDelete)
End If
Next
tr.Commit()
ed.Regen()
End Using
End Using
Catch ex As Exception
ed.WriteMessage("\nImpossible de supprimer la présentation " & LayoutNameToDelete & ": " & ex.Message)
bReturnValue = False
End Try
Return bReturnValue
End Function
I had to make three adjustements. First I replace the openmode to ForWrite and second commit the transaction. Third I also have to lock the document. All the transaction is embed in a try statement for more security.
Hope this help for vb programmer.
Regards,
André
Hi ArtVegas,
I have a question for you. I have a routine to rename a layout and now a good routine to delete layout. I want to rename a layout sequence order. I already have a routine inserting a layout (ex. insert layout 003, the existing layout 003 is rename to 004 and all subsequent layout). My problem is when I delete a layout. I want to do the same thing in reverse order (ex. delete layout 003, renaming layout 004 to 003, etc.).
When I delete layout, i'm using LayoutToBeRemoved to do some processing when I delete a layout, but when I want to rename a layout I have an error saying "Layout already exist". It is ok, because at this event occurs the layout is not deleted. I tried the same thing with LayoutRemoved event, but I have the same problem. But at this stage the layout is supposed to be removed.
Do you have any idea?
Regards,
André
Hi Andre, I'm not exactly sure what you are trying to do. But it sounds as if AutoCAD is complaining that you are trying to rename a layout to a name that is already being used by another layout, which you wouldn't want to do. Perhaps if you post some simple code that shows the problem I'd get a better understanding and might be able to help.
Hi ArtVegas,
I will try to better explain my question. I have a drawing with layout named SEQ001, SEQ002, SEQ003, SEQ004, ... I develop a palette with some buttons. On that palette I have a button to add a new layout. If the user add a layout number 3, I actually rename layout SEQ004 to SEQ005, SEQ003 to SEQ004 and insert a new layout named SEQ003. On the same palette I have another button to delete a layout. The current layout is delete and the subsequent layout are rename. Ex. suppressing SEQ003, SEQ004 is rename to SEQ003 and SEQ005 rename to SEQ004. Hera you will find the sub to rename sequences:
Private Sub RenameSequence(ByVal SequenceNo As String, Optional ByVal Decrement As Boolean = False)
Dim n As Integer = 0
Dim i As Integer = 0
Dim objSequence As PlanGeneration.RinsePath.Sequence = Nothing
Dim strNewLayoutName As String = ""
n = m_Sequences.SequenceList.Count ' m_sequences is a member variable containing information about layout
If Decrement = False Then
For i = n - 1 To 0 Step -1
objSequence = m_Sequences.SequenceList(i)
strNewLayoutName = "SEQ" & String.Format("{0:d3}", (CInt(objSequence.SeqNo.Substring(3)) + 1))
RenameLayout(objSequence.SeqNo, strNewLayoutName)
'I also rename some layers
RenameLayer(objSequence.SeqNo & "-BORNE", strNewLayoutName & "-BORNE")
RenameLayer(objSequence.SeqNo & "-ROUTE", strNewLayoutName & "-ROUTE")
RenameLayer(objSequence.SeqNo & "-VANNE_OUVERTE", strNewLayoutName & "-VANNE_OUVERTE")
RenameLayer(objSequence.SeqNo & "-VANNE_FERMEE", strNewLayoutName & "-VANNE_FERMEE")
If objSequence.SeqNo.ToUpper = SequenceNo.ToUpper Then
Exit For
End If
Next i
Else
Dim j As Integer = GetSequenceOrder(SequenceNo)
For i = j To n - 1
objSequence = m_Sequences.SequenceList(i)
strNewLayoutName = "SEQ" & String.Format("{0:d3}", (CInt(objSequence.SeqNo.Substring(3)) - 1))
RenameLayout(objSequence.SeqNo, strNewLayoutName)
'I also rename some layers
RenameLayer(objSequence.SeqNo & "-BORNE", strNewLayoutName & "-BORNE")
RenameLayer(objSequence.SeqNo & "-ROUTE", strNewLayoutName & "-ROUTE")
RenameLayer(objSequence.SeqNo & "-VANNE_OUVERTE", strNewLayoutName & "-VANNE_OUVERTE")
RenameLayer(objSequence.SeqNo & "-VANNE_FERMEE", strNewLayoutName & "-VANNE_FERMEE")
objSequence.SeqNo = strNewLayoutName
Next
End If
End Sub
'Now the function to rename layout
Private Function RenameLayout(ByVal oldLayoutName As String, ByVal NewLayoutName As String) As Boolean
Dim bReturnValue As Boolean = True
Dim oLayoutIDToRename As ObjectId = Nothing
Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
Dim Db As Database = ed.Document.Database()
Dim acDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
oLayoutIDToRename = GetLayoutByName(oldLayoutName)
Try
Using doclock As DocumentLock = acDoc.LockDocument
Using tm1 As Transaction = Db.TransactionManager.StartTransaction()
Dim oLayoutToRename As Layout = tm1.GetObject(oLayoutIDToRename, OpenMode.ForWrite)
oLayoutToRename.LayoutName = NewLayoutName
tm1.Commit()
oLayoutToRename = Nothing
End Using
End Using
Catch ex As Exception
ed.WriteMessage("\nImpossible de renommer la présentation " & oldLayoutName & " à " & NewLayoutName & ": " & ex.Message)
bReturnValue = False
End Try
Return bReturnValue
End Function
When I use the button all is working well. But if the user delete manually the layout I want to use an event and rename the sequence too.
First I used this event:
<CommandMethod("AddLayoutToBeRemoved")> _
Public Sub AddLayoutToBeRemoved()
AddHandler LayoutManager.Current.LayoutToBeRemoved, _
AddressOf onLayoutToBeRemoved
End Sub
Private Sub onLayoutToBeRemoved(ByVal senderObj As Object, _
ByVal docLayoutEvtArgs As Autodesk.AutoCAD.DatabaseServices.LayoutEventArgs)
Dim strNextLayoutName As String = ""
Dim bRenameLayout As Boolean = False
If docLayoutEvtArgs.Name.ToUpper.StartsWith("SEQ") Then
If GetLayoutList.Count > 1 Then
'If m_CurrentSequencePosition < m_Sequences.SequenceList.Count - 1 Then
' strNextLayoutName = m_Sequences.SequenceList(m_CurrentSequencePosition + 1).SeqNo
' bRenameLayout = True
'End If
'Delete layers associated
If DeleteLayer(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo & "-BORNE", True) = False Then bRenameLayout = False
If DeleteLayer(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo & "-ROUTE", True) = False Then bRenameLayout = False
If DeleteLayer(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo & "-VANNE_OUVERTE", True) = False Then bRenameLayout = False
If DeleteLayer(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo & "-VANNE_FERMEE", True) = False Then bRenameLayout = False
m_Sequences.SequenceList.RemoveAt(m_CurrentSequencePosition)
If m_CurrentSequencePosition > m_Sequences.SequenceList.Count - 1 Then
m_CurrentSequencePosition = m_Sequences.SequenceList.Count - 1
End If
If m_CurrentSequencePosition < 0 Then
SetCurrentLayoutTab("Model")
Else
SetCurrentLayoutTab(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo)
End If
'If bRenameLayout = True Then
' RenameSequence(strNextLayoutName, True)
' SetCurrentLayoutTab(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo)
'End If
End If
End If
End Sub
I realized this event occur before the layout is really deleted, so I understand why renaming my sequence doesn't work. I put some code in comments because it doesn't work. So I found this other event:
<CommandMethod("AddLayoutRemoved")> _
Public Sub AddLayoutRemoved()
AddHandler LayoutManager.Current.LayoutRemoved, _
AddressOf onLayoutRemoved
End Sub
Private Sub onLayoutRemoved(ByVal senderObj As Object, _
ByVal docLayoutEvtArgs As Autodesk.AutoCAD.DatabaseServices.LayoutEventArgs)
Dim strNextLayoutName As String = ""
If docLayoutEvtArgs.Name.ToUpper.StartsWith("SEQ") Then
If GetLayoutList.Count > 1 Then
If m_CurrentSequencePosition < m_Sequences.SequenceList.Count - 1 Then
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.Regen()
strNextLayoutName = m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo
RenameSequence(strNextLayoutName, True)
SetCurrentLayoutTab(m_Sequences.SequenceList(m_CurrentSequencePosition).SeqNo)
End If
End If
End If
End Sub
But I have the same error. When this event occurs the layout does't seem to be deleted too. So I don't know which event to use.
So actually, my user must absolutely use the delete layout button. If not, the sequence will be interrupt. If I don't find a solution, I will add another button to correct the sequence but it is not the way I preferred.
Thank you very much for your help.
Regards,
André
Hi Andre,
I noticed that your RenameSequence() function's "for-loop" seems to rename layouts from the largest number to the smallest (i.e. SEQ005 first, then SEQ004, then SEQ003, etc).
Lets say your user deletes SEQ003. Then the first thing your code appears to do is try to rename SEQ005 to SEQ004, but you can't because SEQ004 already exists. Perhaps this is the problem?
What you should do is get the number of the layout that is removed (say SEQ003), next rename SEQ004 to SEQ003, then rename SEQ005 to SEQ004, then SEQ006 to SEQ005, etc.
It's different when you add a layout. Say you're going to add a layout which will become SEQ003. In that case you should start at the biggest number (as per your for-loop). Here you would rename SEQ005 to SEQ006, then SEQ004 to SEQ005, then SEQ003 to SEQ004 and then add the new SEQ003.
You get what I mean?
Art
Hi Art,
In my RenameSequence I have an argument Decrement and I have two loops depending of the state of this argument. When I add a layout, Decrement is false, so I begin with the higher sequence number and add 1 to the name. If I delete a layout, Decrement is set to true. I delete the layout and the next sequence is lower by 1 and so on. This routine is working well when I used your function to explicitly delete the layout. But when I start this function from the LayoutRemoved or LayoutToBeRemoved event, it doesn'work because, I don't know why, the layout is not already deleted so when I try to rename SEQ004 to SEQ003 (the one I deleted manually using AutoCAD command) SEQ003 already exist.
The problem is with the event. Maybe it is a bug in AutoCAD API.
Thank you,
André
Sorry I didn't notice the decrement argument.
I created a simple test in C# for the LayoutRemoved event below. For simplicity this is based on layout names being numbers only, i.e. 001, 002, 003, 004, 005, etc.
This is not "well-coded" as it assumes that the order of the layouts in the layout-dictionary is sequential which may not always be the case, but this could easily be accounted for with a bit more work.
This worked for me when I tested it. So I don't think it's an AutoCAD bug. Could it be something else in your program?
// Command. [CommandMethod("RegisterLayoutRemoved")] static public void RegisterLayoutRemoved() { // Register layout removed event. LayoutManager.Current.LayoutRemoved += new LayoutEventHandler(Current_LayoutRemoved); } // Layout removed event handler. static void Current_LayoutRemoved(object sender, LayoutEventArgs e) { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // Get removed layout number from layout name. int removedNum; if (!Int32.TryParse(e.Name, out removedNum)) { throw new System.Exception("Could not obtain layout number..."); } using (Transaction tr = db.TransactionManager.StartTransaction()) { // Iterate layout dictionary. DBDictionary layoutDict = (DBDictionary)tr.GetObject( db.LayoutDictionaryId, OpenMode.ForRead, true); foreach (DBDictionaryEntry item in layoutDict) { // Layout (for read). Layout layout = (Layout)tr.GetObject(item.Value, OpenMode.ForRead); // Try get layout number. int i; if (Int32.TryParse(layout.LayoutName, out i)) { // If layout number is greater than for layout removed. if (i > removedNum) { // Then decrement layout number. layout.UpgradeOpen(); layout.LayoutName = String.Format("{0:D3}", (i - 1)); } } } tr.Commit(); } }
Thank you very much Art,
I will try that and I will give you some feedback.
Have a good day,
André
Below is a partial of a routine that I use in VBA to remane layouts.
You should first remane the layouts, so you don't have have a conflict about remaning layout to existing layouts.
I first added temp to the layout name.
Then I use the TabOrder property of the layout to get the order of the layouts.
Now you can reanme the layout to a sequence of names based on the TabOrder of the layouts.
http://www.theswamp.org/index.php?action=search2
**************** vba routine not vb.net *****************************
For Each objLayout In objLayouts
lngCnt = lngCnt + 1
strLayoutName = objLayout.Name
If strLayoutName <> "Model" Then
lngTabOrder = objLayouts(objLayout.Name).TabOrder
lngTabOrder = lngTabOrder + (formRenumber.TxtBoxStartNumber.Value - 1)
objLayout.Name = "tempLayout" & lngTabOrder
DoEvents
End If
Call ProgUpdate(lngCnt)
DoEvents
Next
Call ProgSetup(objLayouts.Count)
formProgRenumLay.Caption = " Re-Naming objLayouts "
lngCnt = 0
For Each objLayout In objLayouts
lngCnt = lngCnt + 1
strLayoutName = objLayout.Name
If strLayoutName <> "Model" Then
objLayout.Name = formRenumber.txtBoxLayName & _
CStr(Mid$(strLayoutName, 11, (Len(strLayoutName) - 10))) ' Rename and renumber objLayout
DoEvents
End If
There is a flaw with this code:
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
// Iterate dictionary entries.
foreach (DBDictionaryEntry de in layoutDict)
{
string layoutName = de.Key;
if (layoutName != "Model")
LayoutManager.Current.DeleteLayout(layoutName); // Delete layout.
}
An item is being deleted from a list that is being interated. I fall into this trap over and over...
A list of names should be collected and iterated instead.
string[] layouts = GetLayoutNames();
foreach (string LayoutName in layouts)
{
if (LayoutName.ToUpper() != "MODEL" || LayoutName.ToUpper() != pstrLayoutToKeep.ToUpper())
LayoutManager.Current.DeleteLayout(LayoutName);
}
I had been using the first code but results were unreliable, particularly when I attempt to rename Layout1 (which it thinks still exists).
Dale
For the first solution, of course, being in a transaction.
But pay attension to his warning: " do not alter collections in a loop unless casted to a list.
Delete layout's using the LayoutManager doesn't require a Commit. A Regen may be required to see the results.
Without the tr.Commit(); I do not see any results, layouts are not erased, with the tr.Commit();, they are.
Can't find what you're looking for? Ask the community or share your knowledge.