Deleting Sheet with VBA crashes Excel - vba

I am trying to delete a worksheet when the user click's on an image (button) in Excel. However this makes excel crash and restart, forgetting any unsaved progress.
This is my sub:
Sub DeletePlan()
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
Dim SheetNamesToCopy As String
SheetNamesToCopy = ActiveSheet.Name
' Check what addon sheets exists for the media, then add existing ones to string
If CheckSheet("periodeplan", True) = True Then
ThisWorkbook.SheetS(SheetNamesToCopy & " - periodeplan").Delete
End If
If CheckSheet("ukesplan", True) = True Then
ThisWorkbook.SheetS(SheetNamesToCopy & " - ukesplan").Delete
End If
If CheckSheet("Input", True) = True Then
ThisWorkbook.SheetS(SheetNamesToCopy & " - Input").Delete
End If
SheetS("Totalplan").Select
ThisWorkbook.SheetS(SheetNamesToCopy).Delete
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
End Sub
The application crashes most of the time. But not always... Any ideas what might be wrong?
(I have tested and confirmed that the delete function causes the crash, but its not always the same sheet).
Edit: This function is not deleting the last sheet in the workbook. There are 20 more sheets. Also i use Application.Calculation = xlCalculationAutomatic, because there are allot of formulas, and i do not want excel to calculate changes before all is connected sheets are deleted.
Any hint or answer is appreciated :)

The error occurs when the button that initiates the macro is located on one of the sheets that are to be deleted.
So the answer is: Do not create a button (or image linked to a macro) that deletes the sheet it is on.
If anybody can add to this answer with a reason for this error, please do so ;)

I just ran into this problem myself! I'm going to defer to more experienced designers on a way to refine this technique, but as a general concept, I do have a working solution:
If you allow the macro to run it's course and then delete the sheet, it doesn't crash. Something like this:
Sub Delete_This_Sheet()
Application.OnTime Now + TimeValue("00:00:02"), "Watergate"
Sheets("Sheet with a death sentence").Visible = False
End Sub
Sub Watergate() 'To make things go away
Application.DisplayAlerts = False
Sheets("Sheet with a death sentence").Delete
Application.DisplayAlerts = True
End Sub

Resurrecting this thread because I had the same issue and want to share the solution.
I had a very simple sub to delete worksheets:
Sub deletetraindoc()
Dim ws As Worksheet
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Sheets
'This if statement looks for any worksheet that contains the term "Form"
'Any worksheet that contains that string will be deleted
If InStr(ws.Name, "Form") > 0 Then
Application.DisplayAlerts = False 'Deactivates the standard deletion confirmation
ws.Activate
ws.Delete 'Deletes the worksheet
Application.DisplayAlerts = True 'Reactivates display alerts
End If
Next
Application.ScreenUpdating = True
End Sub
This inconsistently caused crashing until I added the line "ws.Activate" to activate each worksheet before deleting, which seems to have resolved the issue. I've run into this problem in many other situations performing actions on worksheets, but it usually would result in an object error instead of a complete crash.

I found that in Office 2013, you cannot place a button that overlaps a cell that that macro changes. Interesting enough, it doesn't occur if the change is numeric in nature, but if it is alphanumeric, it blows up excel when you attempt to delete that tab. Turns out, it blows it up when attempting to delete the tab manually (by mouse click) or by the macro attempting to do it. THUS, my lesson learned from this thread and applying it to my specific situation is to never place a development button over the cell it changes (in my case, it was simply a cell that gives the status of what that macro was doing). Excel 2013 does not like that situation while Excel 2010 simply didn't care.

I do believe you nare right and the only way around this is to ensure this macro is on the total plan sheet. Also you're doing a few unnecessary steps and the select a sheet should be to activate and select a cell.
Sub DeletePlan()
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
Dim SheetNamesToCopy As String
SheetNamesToCopy = ActiveSheet.Name
'dont delete total plan
If sheetnames = "Totalplan" then exit sub
SheetS("Totalplan").Activate
Activesheet.Cells(1,1).select
'Turn off errors if sheet doesn't exist
On error resume next
ThisWorkbook.SheetS(SheetNamesToCopy & " - periodeplan").Delete
ThisWorkbook.SheetS(SheetNamesToCopy & " - ukesplan").Delete
ThisWorkbook.SheetS(SheetNamesToCopy & " - Input").Delete
ThisWorkbook.SheetS(SheetNamesToCopy).Delete
On error goto 0
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
End Sub

You can delete the active sheet from a button (or image) on the active sheet. You just have to work around it.
ActiveSheet.Move before:=Worksheets(1)
Worksheets(2).Activate
Worksheets(1).Delete

I had a similar, but not identical problem. I had a macro that deleted all the chart sheets with the following command, but although it operated correctly, Excel 2013 was doomed to fail as soon as I tried to save the file, and would "recover" by reverting to the previously saved situation, throwing away any subsequent work:
Oh, and it worked fine until I moved from, I think it was, Excel 2010 to 2013, changing to Windows 10 at the same time.
The command of doom was:
ThisWorkbook.Charts.Delete
Thanks to some inspiration from the answers above, I first inserted a save before the deletion action and then changed the delete as follows (it turned out the saves were not after all needed but I like to have them there, even if I comment them out):
Dim graphSheet As Chart
ActiveWorkbook.Save
For Each graphSheet in this Workbook.Charts
graphSheet.Delete
ActiveWorkbook.Save
Next graphSheet
I should also mention that there is a preceeding Application.DisplayAlerts = False before the for loop and of course the Application.DisplayAlerts = True after the Next... statement to cut out the unwanted
are you sure you want to do this type question?
Again, thanks to your contributors for the inspiration.

I wanted a button that would delete a sheet, as the workbook was protected and could 'export' results but couldn't delete unwanted results.
My simple workaround was to have the macro hide the sheet, but then to delete the last hidden sheet, so the files dont become huge with dozens of hidden sheets.
I created a range in a hidden sheet called "DeleteSheet", to store the name of the hidden sheet.
Sub Delete_Sheet()
ActiveWorkbook.Unprotect Password:="Patrick2017"
ActiveSheet.Unprotect Password:="Patrick2017"
On Error Resume Next
' (In event there is no hidden sheet or the sheet is already deleted, resume next)
'The below finds the name of the previously hidden sheet to delete, and stores it.
Dim DeleteSheet As String
DeleteSheet = Range("DeleteSheet")
'The below is to avoid the main sheet being deleted
If ActiveSheet.Name = "POAL Calculator" Then
Exit Sub
End If
' The below stores the current sheets name before hiding, for deleting next time the
' macro is run
Range("DeleteSheet") = ActiveSheet.Name
ActiveWindow.SelectedSheets.Visible = False
' The below deletes the sheet previously hidden
Application.DisplayAlerts = False
Sheets(DeleteSheet).Delete
ActiveWorkbook.Protect Password:="Patrick2017"
Application.DisplayAlerts = True
End Sub

How about moving the button code to a module?
I have had an issue with that in Excel 2016 whereby Option explicit didn't work if the code was in a module, but if the code is in a module, then you 'should' be able to delete the sheet where the button was.

Related

Worksheet_Change with manual calculation (Application.Calculation = xlManual)

I want Excel to calculate the sheet, every time the user enters a value (source1 source2):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Worksheet("Calc").Range("R1:R100")
MsgBox "Check!"
Application.EnableEvents = False
If Not Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
' Display a message when one of the designated cells has been changed.
MsgBox "Cell " & Target.Address & " has changed."
'Calculate
Sheets("Calc").Calculate
End If
Application.EnableEvents = True
End Sub
The sheet does not get recalculated after entry of value. Even, the message box "Check!" never appears. - Why is that?
(For info: I can only guess that it has something to do with my setting the calculation to "manual":
I have a sheet in Workbook "B.xlsm" with circular references, hence I run a script from Workbook "A.xlsm", from where I open Workbook "B.xlsm":
Application.Calculation = xlManual
[...]
Set wb = Workbooks.Open(strPath)
Is there a way to get this method "Change" to work with manual calculation?)
I think Tim nailed it.
At some point in testing the code, the code stopped; Either from and Run-time Error or manually stepping through line-by-line. In either case, you probably changed something which forced it to reset and it never had a chance to run the most important line...Application.EnableEvents = True.
Disabling Events is a persistent state.
It's very wise to make sure that you have error handling working to restore it, if other users are going to be using your code. Unfortunately, you'll still break it while testing.
I use the following routine (at the top of the main module) to fix it, when this happens:
Public Sub FixIt()
Application.EnableEvents = True
If Not ActiveWindow Is Nothing Then Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
In fact, I usually call this routine as the first line of any button I have on the Ribbon/Sheet, just to make sure that everything is working like it should be.
In your case, you could omit the Application.Calculation line, or set it equal to xlManual

Unable to set Application.ScreenUpdating to false

I have some VBA code as follows:
Sub copyData(fromRange as Range, toRange as Range)
Application.ScreenUpdating = False
<copy paste code here>
Application.ScreenUpdating = True
End Sub
Even though I am setting Application.ScreenUpdating to False, it remains at True. I have verified this using F8 and hovering over Application.ScreenUpdating (it shows True).
My copy paste code works. It switches worksheets but since ScreenUpdating remains at True, I can see the screen flicker.
Is there a way to set Application.ScreenUpdating to False?
P.S. I saw a similar question in this forum but there was no concrete resolution to it.
Any help will be greatly appreciated!
I've had the same issue for quite a while, and I figured out something: Application.screenUpdating only stays FALSE for how ever long a macro runs. When any macro running stops, it turns True. You can try this:
Sub testApplicationScreenUpdating()
Application.ScreenUpdating = False
Debug.Print "Application screen updating is:" & Application.ScreenUpdating
Application.ScreenUpdating = True
End Sub
if you just run this, it will return in the Immediate window: "Application screen updating is:False"
if you run it step by step, and hover over Applicaiton.ScreenUpdating with your mouse, it will show as "True", even if the Immediate window will show "False".
if you comment out the [Application.ScreenUpdating = True] at the end, and then run [Debug.Print "Application screen updating is:" & Application.ScreenUpdating] separately, it will return true, even if it was not switched to true.
Try this code and see the values for each in the Immediate Window Ctrl+G:
Sub copyData()
Dim r As Boolean
r = Application.ScreenUpdating = False
Debug.Print "'Application.ScreenUpdating' is set to " & r
r = Application.ScreenUpdating = True
Debug.Print "'Application.ScreenUpdating' is set to " & r
End Sub
I use Excel as part of Microsoft 365. I too fought with the screen flickering problem. Although my macro worked, the flickering was very annoying. I tried several approaches and stumbled upon this:
Minimize the second workbook before initiating the macro from the first workbook. For my situation, the screen no longer flickered. I also tried the following code to minimize the second workbook from within VBA. If the second workbook was already minimized, there was no effect. If the second workbook was not minimized, the screen flickered only once - to enable me to minimize the second workbook. Subsequent switching back and forth between workbooks did not introduce any screen flickering.
‘
Filename = "SecondWorkbookName.xlsx"
Windows(Filename).Activate
Application.WindowState = xlMinimized ' Minimize workbook to prevent flickering.
Application.ScreenUpdating = False

How to disable add sheet button in Excel with VBA?

I have a workbook I would like to disable the Add New Sheet button that is next to the tabs. I have searched and found the following that disable the insert options on the workbook book which is great.
Application.CommandBars("Ply").FindControl(, 945).Enabled = False
Application.CommandBars("Insert").Controls(4).Enabled = False
But I have yet to find the command for the Add New Sheet button. Is there a place that lists all these options or a tool I can use to identify the control or button.
The workbook is shared so automatically deleting the sheet on creation will not work.
Protect Structure does not work either and throws the following error:
In the ThisWorkbook code sheet, paste the following.
Option Explicit
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Application.DisplayAlerts = False
Sh.Delete
End Sub
Any new (or copied) worksheet that is created is instantly deleted.
This isn't a proper solution to your actual problem, but it will give the illusion of doing what you want...
In the ThisWorkbook module, add this to instantly hide any new sheets with 'DeleteThisSheet' in cell A1
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Sh.Cells(1, 1) = "DeleteThisSheet"
Sh.Visible = xlSheetVeryHidden
End Sub
Obviously, this will clutter up the workbook with hidden sheets, so you can clear them out from time to time using this code to switch off sharing and delete the hidden sheets.
Sub removeSheets()
Dim ws As Worksheet
' Turn off sharing
Application.DisplayAlerts = False
If ActiveWorkbook.MultiUserEditing Then
ActiveWorkbook.ExclusiveAccess
End If
' Delete veryhidden sheets with delete code
For Each ws In ThisWorkbook.Sheets
If ws.Visible = xlSheetVeryHidden And ws.Cells(1, 1) = "DeleteThisSheet" Then
ws.Visible = xlSheetVisible
ws.Delete
End If
Next ws
' Turn sharing back on
If Not ActiveWorkbook.MultiUserEditing Then
ActiveWorkbook.SaveAs ActiveWorkbook.Name, accessmode:=xlShared
End If
Application.DisplayAlerts = True
End Sub
As I say, it's not ideal, but may at least serve your purpose, although probably won't stand up to many people repeatedly attempting to add new sheets. You could possibly add a msgbox to the newssheet code to say something along the lines of 'This action has been disabled' to stop them retrying. I'll keep an eye on this thread to see if anyone comes up with a proper solution, it's always good to learn something new.
Not sure if this solves the issue:
Application.CommandBars("Insert").Controls(4).Visible = False
This uses Visible property.

Excel Macro for Updating log

I am looking to create a macro where I take what I have in a master file that is constantly being updating on the last row and placing this into a separate log file.
I would only like to copy specific cells from the last row of the original and paste them into the log file, i've gotten close I believe but need help with the following areas:
Selection of the specific columns in the last row
How to not have the log file open or activated at any point for this to happen when the Macro is run.
I apologize if this question has been answered but I can't seem to curate all the information I am finding online to an applicable solution. Here is the macro I have thus far...
' copy2 Macro
'
Range("B5000").End(xlUp).Select
Selection.EntireRow.copy
Windows("Daily Referral Log.xlsx").Activate
Range("A65536").End(xlUp).Offset(1, 0).Select
ActiveSheet.Paste
End Sub
Looks like you're trying to open the log workbook and paste the last row from a data file into it.
I think this is what you're looking for.
Please let me know if you need something else.
How to not have the log file open or activated at any point for this to happen when the Macro is run.
Regarding this line, I think I am understanding you right.
You don't want to have to open it yourself manually; you want the code to do it.
If that's not what you mean, please let me know (you will have to lock the file for write access to modify it regardless).
Sub CopyLastRowToLog()
Dim ThisWb, wb As Workbook
Set ThisWb = ThisWorkbook
Application.EnableEvents = False
Application.DisplayAlerts = False
Application.ScreenUpdating = False
On Error Resume Next
Set wb = Workbooks.Open("FileName and Path Goes Here") 'Edit This Line
On Error GoTo 0
'Basic Error Handling - Probably worth adding ReadOnly check but keeping code short
If wb Is Nothing Then
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
MsgBox ("Error Opening File")
Exit Sub
End If
'Put the names of the sheets you're using below.
'The first 2 sheets should be the sheet you're copying from
'The second 2 sheets should be the sheet you're copying to
ThisWb.Sheets("Sheet1").Range("A" & Sheets("Sheet1").UsedRange.Rows.Count).EntireRow.Copy _
wb.Sheets("Sheet1").Range("A" & Sheets("Sheet1").UsedRange.Rows.Count + 1)
wb.Close (True)
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Edit:
To answer your first question, the file name looks weird:
Workbooks.Open("C:\Users\EYousefi\Desktop[Daily Referral Log.xlsx]Sheet1")
It should probably be:
Workbooks.Open("C:\Users\EYousefi\Desktop\Daily Referral Log.xlsx")
You don't need to specify the sheet there.
Secondly, to copy specific columns, you would want to change the copy line.
I added 2 new variables to make it easier to read:
'Get the last rows for the workbooks
Dim ThisWBLR, FinalWBLR
ThisWBLR = ThisWB.Sheets("Sheet5").UsedRange.Rows.Count
FinalWBLR = wb.Sheets("Sheet1").UsedRange.Rows.Count+1
ThisWb.Sheets("Sheet5").Range("B" & ThisWBLR & ":D" & ThisWBLR).Copy _
wb.Sheets("Sheet1").Range("B" & FinalWBLR)
You can also specify individual pieces of data one at a time if it is easier for you:
'Copy B to B
ThisWB.Sheets("Sheet5").Range("B" & ThisWBLR).Copy wb.Sheets("Sheet1").Range("B" & FinalWBLR)
'Copy C to C
ThisWB.Sheets("Sheet5").Range("C" & ThisWBLR).Copy wb.Sheets("Sheet1").Range("C" & FinalWBLR)

Printing faster in Excel

The print functionality of Excel (using VBA) is extremely slow. I'm hoping someone has a way of speeding the printing up (without using the Excel 4 Macro trick). Here's how I do it now:
Application.ScreenUpdating = False
With ActiveSheet.PageSetup
-various setup statements which I've already minimized-
End With
ActiveSheet.PrintOut
Application.ScreenUpdating = True
Yes, the PageSetup properties are very slow when you set them.
You have already set Application.ScreenUpdating = False, which is good, but an equally (or more) important step in this case is to set Application.Calculation = xlCalculationManual. (It is best if you save these settings and then restore them to the original at the end.)
Additionally, the property get for each PageSetup property is very fast, while it is only the property set that is so slow. Therefore, you should test the new property setting to make sure it isn't already the same as the existing property value in order to prevent an unnecessary (and expensive) call.
With all this in mind, you should be able to use code that looks something like the following:
Dim origScreenUpdating As Boolean
origScreenUpdating = Application.ScreenUpdating
Application.ScreenUpdating = False
Dim origCalcMode As xlCalculation
origCalcMode = Application.Calculation
Application.Calculation = xlCalculationManual
With ActiveSheet.PageSetup
If .PrintHeadings <> False Then .PrintHeadings = False
If .PrintGridlines <> False Then .PrintGridlines = False
If .PrintComments <> xlPrintNoComments Then .PrintComments = xlPrintNoComments
' Etc...
End With
Application.ScreenUpdating = origScreenUpdating
Application.Calculation = origCalcMode
Edit: A couple of updates:
For Excel 2010 and above you can make use of the 'Application.PrintCommunication' property, while for Excel 2007 and below, you can make use of 'ExecuteExcel4Macro'. For more details, see Migrating Excel 4 Macros to VBA.
For Excel 2007 and below, another interesting trick is to temporarily assign the printer driver to the 'Microsoft XPS Document Writer' and then set it back. Printing speed can improve by 3x. See: Slow Excel PageSetup Methods.
Hope this helps...
In furthering Michael's post and answering #rhc's question, the following code may also help you if need to copy Page Setup customizations from a single worksheet to multiple worksheets in a workbook:
Public Sub CopyPageSetupToAll(ByRef SourceSheet As Worksheet)
' Raise error if invalid source sheet is passed to procedure
'
If (SourceSheet Is Nothing) Then
Err.Raise _
Number:=vbErrorObjectVariableNotSet, _
Source:="CopyPageSetupToAll", _
Description:="Unable to copy Page Setup settings: " _
& "invalid reference to source sheet."
Exit Sub
End If
SourceSheet.Activate
With SourceSheet.PageSetup
' ...
' place PageSetup customizations here
' ...
End With
SourceSheet.Parent.Worksheets.Select
Application.SendKeys "{ENTER}", True
Application.Dialogs(xlDialogPageSetup).Show
End Sub
Alternatively, you could also modify the procedure to create a temporary worksheet to host your Page Setup changes, and then propagate those changes out to the other worksheets in your workbook:
Public Sub CopyPageSetupToAll(ByRef SourceBook As Workbook)
Dim tempSheet As Worksheet
' Raise error if invalid workbook is passed to procedure
'
If (SourceBook Is Nothing) Then
Err.Raise _
Number:=vbErrorObjectVariableNotSet, _
Source:="CopyPageSetupToAll", _
Description:="Unable to copy Page Setup settings: " _
& "invalid reference to source workbook."
Exit Sub
End If
Set tempSheet = SourceBook.Worksheets.Add
tempSheet.Activate
With tempSheet.PageSetup
' ...
' place PageSetup customizations here
' ...
End With
SourceBook.Worksheets.Select
Application.SendKeys "{ENTER}", True
Application.Dialogs(xlDialogPageSetup).Show
tempSheet.Delete
Set tempSheet = Nothing
End Sub
Due to the use of the SendKeys() function and Application.Dialogs functionality, this code does not offer the cleanest possible solution. However, it gets the job done. :)
if you want to have basicly the same page settings for every tab in a workbook can you speed things up by setting up one workshet and then copying that worksheet's settings somehow to the other worksheets? Is this possible?