I have an excel file which iterates over different excel files to update them and create a PDF file from them.
The macro iterates over rows and opens the files. This code opens the files:
Private Sub openFile(row As Integer)
Dim filePath As String
Dim wb As Workbook
filePath = Application.ActiveWorkbook.Path + "\" + allRows(row).filename
Set wb = Workbooks.Open(filePath)
Application.Run "RefreshEntireWorksheet"
Application.OnTime Now + (TimeSerial(0, 1, 59)), "'ThisWorkbook.updateCharts """ & row & "'"
Application.OnTime Now + (TimeSerial(0, 2, 59)), "'ThisWorkbook.createEmail'"
End Sub
Now if there are multiple files, only 1 file has the charts properly updated (the file being on the front). It seems like the updateCharts code doesn't work at all:
Sub updateCharts(row As Integer)
Dim SheetName As String
Dim wb As Workbook
SheetName = "Sheet1"
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
Set wb = Workbooks(allRows(row).filename)
wb.Activate
wb.Sheets(SheetName).Activate
For Each cht In wb.Sheets(SheetName).ChartObjects
cht.Chart.Refresh
DoEvents
Next cht
End Sub
I checked following links for answers, but none helped:
Can't refresh a chart in Excel VBA (Excel 2016)
Refresh all charts without blinking
excel chart not updating when data changed by vba
Excel 2013 vba refresh chart content after each iteration of For Loop
They all suggest to add an DoEvent, but that doesn't work. So the question is, how can I update charts in a different workbook via VBA?
I have encountered similar issues in the past, and have observed that they seem to increase as the Version of Excel increments; I suspect it is a case of falling afoul of side-effects caused efficiency enhancements elsewhere to speed up the Application in general (reducing unnecessary drawing on the screen, et cetera)
One thing that I have found (sometimes) works is to try and force the Application Window to Refresh/Redraw, by setting the WindowState to itself:
Application.WindowState = Application.WindowState
This is similar to how some people suggest using ActiveWindow.SmallScroll down:=0, ActiveWindow.SmallScroll 0, ActiveWindow.SmallScroll, or ActiveWindow.SmallScroll down:=1: ActiveWindow.SmallScroll up:=1
Combining this with the ScreenUpdating toggle, and the DoEvents, you can create a quick Subroutine like this:
Public Sub RedrawScreen()
Dim ScreenUpdating As Boolean
ScreenUpdating = Application.ScreenUpdating
Application.ScreenUpdating = True
Application.WindowState = Application.WindowState
DoEvents
Application.ScreenUpdating = ScreenUpdating
End Sub
Try this (it works for me - Excel 2016):
DoEvents must be in your code as others said before
Delete all colours of the cells of the spreadsheet: all the background of the cells must be "no background"; use only automatic colour (black) for the text of all the cells.
Save, close and reopen the file. Now start your macro and look at your chart.
I don't know why it works, but.........it works!
For those more capable than me: do the opposite: if your chart is updating with your macro running, try to colour some text or some cells. The chart will not update anymore. The Microsoft experts should check what is happening.
Hope this helps. Bye, bb.
Related
I wrote several macros for workbook and I have noticed that all the macros run but then at the last action, Excel crashes.
After much troubleshooting, I found the trigger: "Activesheet.move" or "Activesheet.delete." It seems whenever a sheet is deleted from a workbook (or moved to a new workbook), Excel crashes.
Below is the final macro which triggers Excel to crash. The workbook (Template.xlsm) has 3 other sheets so deleting one should not prompt it to close. Essentially, I press the "FinishButton" which is assigned the SaveTemplate() macro. The Macro makes sure there is data in D8 before continuing. It then deletes the "FinishButton" before deleting the activesheet.
Can someone please advise what I am doing wrong or what is causing the crash?
Sub SaveTemplate()
Dim dtToday As String
Dim workdoc As String
dtToday = Format(Date, "mm_dd_yyyy")
workdoc = "Working_Doc_" & dtToday
Workbooks("Template.xlsm").Activate
If Sheets(workdoc).Range("D8").Value = "" Then Exit Sub
Sheets(workdoc).Unprotect Password:="password"
Sheets(workdoc).Shapes.Range(Array("FinishButton")).Select
Selection.Delete
Sheets(workdoc).Protect Password:="password"
Application.DisplayAlerts = False
Sheets(workdoc).Delete
Application.DisplayAlerts = True
Workbooks("Template.xlsm").Save
End Sub
Thank you
After 10 years, I find myself making my first post. I've search endlessly for a solution to no avail. I want to:
copy a range of cells
save and close the workbook
user can paste to another application
I haven't even closed the workbook in the code below, yet there's nothing on the clipboard after the SaveAs.
Sub Test()
Application.CutCopyMode = True
ActiveWorkbook.Sheets(1).Range("A1:C5").Copy
ActiveWorkbook.SaveAs Filename:="F:\Test.xlsx"
End Sub
Setting CutCopyMode to False yields the same results. I can paste no problem if I exclude the SaveAs line.
Since there are no changes to the workbook after you save, you can simply switch the lines and get your desired end result.
Sub Test()
Application.CutCopyMode = True
ActiveWorkbook.SaveAs Filename:="F:\Test.xlsx"
ActiveWorkbook.Sheets(1).Range("A1:C5").Copy
End Sub
I have a macro in Excel 2019 which runs in less than one second directly through VBE (by pressing F5), or when I configure a button to the macro in the Ribbon (through options>customize ribbon).
When I create a button (FormControlButton) inside the sheet area, and associate the macro, it takes at least seven seconds.
The macro runs without any error message. Other macros are slower as well, but this one is the most noticeable.
My macro builds a jagged array with data (~4000 records) that is in another sheet, then sorts the array by bubble/quicksorting (tested both to check if the problem could be here, and it wasn't), then filters it and returns data in a new sheet.
The macros where designed in Excel 2010, and I noticed the problem right after our company updated Microsoft Office from 2010 to 2019. (Windows was updated the same day from 2007 to 10, but I think the problem is in Excel, as I tested it again in some PCs that still had Office 2010 and the macros worked as fast as if run through VBE). Creating and editing macros is not prohibited by administrators.
Adding more information as requested:
I didn't add code because it's not a problem of a specific macro, but I noticed the ones that slowed most are the ones that interact with arrays. Besides that, as it didn't happen when I used buttons inside a sheet in Office 2010, maybe it's a bug in Office 2019.
One thing in common in all my macros is that I follow Microsoft's recommendations to speed up macros, and I use this chunk of code:
Sub SubName()
Call DeactivateSystemFunctions
'Rest of the code
Call ReactivateSystemFunctions
End Sub
Where
Sub DeactivateSystemFunctions()
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.ActiveSheet.DisplayPageBreaks = False
Application.EnableEvents = False
End Sub
Sub ReactivateSystemFunctions()
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.Calculation = xlCalculationAutomatic
Application.ActiveSheet.DisplayPageBreaks = True
Application.EnableEvents = True
End Sub
I don't use .activate or .select in any of my macros, and while formatting I always try to put the max inside a With/End With.
My macro was working fine through VBE but taking too many time when activated through a FormControlButton inside my sheet. As #RonRosenfeld suggested, I had to set a timer to each specific part of my code to find where the problem was. I put the timer at the beggining of my code and I had to move the command to stop the timer to each part of it until I found where it was getting slow.
My macro creates a jagged array and then sorts it through Quicksorting, and as the quicksort I made takes more than one criteria to sort, I thought the problem might be occurring there, as it is a recursive method.
But actually the problem was happening when I was printing the results of the sorted jagged array in another worksheet I create using the same macro.
I print data this way:
NewSheet.Cells(NewSheetRow, Column1) = SortedArray(RecordNumber)(DesiredInfo1 - 1)
NewSheet.Cells(NewSheetRow, Column2) = SortedArray(RecordNumber)(DesiredInfo7 - 1)
NewSheet.Cells(NewSheetRow, Column3) = SortedArray(RecordNumber)(DesiredInfo14 - 1)
'As my jagged array is built with data from a Source Worksheet:
'RecordNumber is the (Row - 1) in the source worksheet
'DesiredInfoX is the Column in the source worksheet
The problem happened only when printing specific Columns. The source sheet has different columns, each with a different data format. The only data format that slowed things down was strings.
I went to the source worksheet and noticed some problems:
As the file went from excel 2000 to 2010 to 2019 and data was not migrated but simply saved from .xls to .xlsm, when I went to the end of the source sheet, I noticed it had only 65536 rows (not 1048576 as expected), but had 16384 columns (last=XFD).
It was only happening with the source sheet, which is the one we have more data in. Other sheets in the same workbook had the expected 1048576 rows and 16384 columns.
After we started using excel 2019, some of the data that was supposed to be String(Text), was formatted as GENERAL/NUMBER. I can't affirm it was not human error, but our source sheet is filled by macro, not by human, and the macro forces formatting of each data.
What I did to solve the problem:
I migrated all data from all sheets, to a new workbook using VBA, not copy/paste. After passing the values to the new source sheet, I forced the formatting of each column. All macros had to be migrated as well.
After that, the FormControlButton inside the sheet is working as fast as activating the macro directly through VBE by pressing F5.
If anybody needs:
'###Timer code
'Got it from https://www.thespreadsheetguru.com/the-code-vault/2015/1/28/vba-calculate-macro-run-time
'Put this part in the beggining of your code
Dim StartTime As Double
Dim SecondsElapsed As Double
'Remember time when macro starts
StartTime = Timer
'Put this part where you want the timer to stop
'Determine how many seconds code took to run
SecondsElapsed = Round(Timer - StartTime, 2)
'Notify user in seconds
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
'###Migration macro:
Sub Migrate()
Call DeactivateSystemFunctions
'Source File
Dim XLApp As Object
Dim WbSource As Object
Dim WsSource As Object
Set XLApp = CreateObject("Excel.Application")
XLApp.Visible = False
Set WbSource = XLApp.Workbooks.Open("C:\FolderFoo\FolderBar\Desktop\SourceFileName.Extension")
Set WsSource = WbSource.Worksheets("SourceWorksheetName")
'Destination File. May be set as source file or if using this workbook by simply:
Dim WsDest As Worksheet
Set WsDest = ThisWorkbook.Worksheets("DestinationSheetName")
Dim BDR As Long
Dim BDC As Long
Dim UltR As Long
Dim UltC As Long
UltR = WsSource.Cells(Rows.Count, 1).End(xlUp).Row
UltC = WsSource.Cells(1, Columns.Count).End(xlToLeft).Column
For BDR = 1 To UltR
For BDC = 1 To UltC
If WsSource.Cells(BDR, BDC) <> vbEmpty Then
WsDest.Cells(BDR, BDC) = WsSource.Cells(BDR, BDC)
End If
Next BDC
Next BDR
'Format your columns as needed
With WsDest
.Columns(Column1Number).NumberFormat = "0"
.Columns(Column2Number).NumberFormat = "dd/mm/yyyy"
.Columns(Column3Number).NumberFormat = "#"
.Columns(Column4Number).NumberFormat = "#"
.Columns(Column5Number).NumberFormat = "0.000"
End With
WbSource.Close SaveChanges:=False
Call ReactivateSystemFunctions
End Sub
TLDR: I had the same problem and I think it's the fault of the mouse pointer.
My solution:
Dim Cursor As XlMousePointer
Cursor = Application.Cursor
Application.Cursor = xlWait
-- YOUR CODE HERE --
Application.Cursor = Cursor
Long Version:
I noticed a similar problem in one of my macros and it seems to occur when lots of cells are manipulated individually (i.e. looping through hundreds or thousands of cells and changing their values).
You might notice the mousepointer changing eratically when you start it from the FormButton or ActiveX Button, but when you start the macro from VBE, it does not do that.
In my case, the macro even started to run faster if I took the focus off the excel window - something as simple as moving the mouse over the windows start-menu button or over an application in the windows taskbar improved the performance of the macro.
I did not research this further, but I concluded that the erratic changes to the mousepointer were in fact the killer to the performance, so I did the following:
Before changing the cells, I set the mousepointer to something other than "Default". When you do that it stays there and does not change while you manipulate the cells. After that I set the mousepointer to whatever status it had before my macro.
I did not have the same problem since.
I have an Excel workbook in which almost everything is automated. It opens other workbooks, copies data from them, closes them and then loops through the data several times to generate reports that we print and use. Almost every time after running all the VBA and then trying to print, print preview gets stuck either finding the printer or loading page sizes as seen in the picture. Closing Excel and reopening the document restores print preview to normal functionality. The only thing related to printing that the VBA does is change the print area. I have not had this issue with any other documents that run VBA. Is this just a bug or possibly something in the code?
This is the code that causes print preview to fail to load in some way. If I skip this section then it works as intended... I would like to have this code function in some way as it's still needed.
Set wb1 = Workbooks.Open(FileName)
Set wb2 = ThisWorkbook
For i = LBound(sArray) To UBound(sArray) 'Loops through array, copies available data from last report
ShtName = sArray(i, 0)
On Error Resume Next
wb1.Sheets(ShtName).Activate
If Err.Number = 0 Then
wb1.Sheets(ShtName).Activate
Columns("A:U").Copy
wb2.Sheets(ShtName).Activate
Columns("BE:BV").Select
Selection.PasteSpecial xlPasteValues
Application.CutCopyMode = False
lastrow = Cells(Rows.Count, "BE").End(xlUp).Row
Range("BA2:BC2").Select
Selection.AutoFill Destination:=Range(Cells(2, "BA"), Cells(lastrow, "BC")), Type:=xlFillDefault
End If
On Error GoTo 0
DoEvents
Next i
wb1.Close False
Sheet2.Activate
I've tried commenting out "On Error Resume Next", "On Error Goto 0", "If...", "End If", and "DoEvents". Print Preview still fails with those removed and just doing the copy and paste from the previous sheet.
It seems that removing all instances of DoEvents has fixed the issue...
Print Preview works correctly and Excel does not crash when exiting the workbook anymore.
I believe you're looking for some type of refresh action that can re-sync the preview display with the data on the sheet. You might want to try this at the end:
ActiveSheet.EnableCalculation = False
ActiveSheet.EnableCalculation = True
If that doesn't work, see if manually saving fixes the issue. You can try calling Application.Save at the end.
"Print preview occurs automatically when you choose File --> Print. Another option is to use the Page Layout view (the icon on the right side of the status bar). To get the old-style print preview, you need to use VBA. The following statement displays a print preview of the active sheet: ActiveSheet.PrintPreview "
Quoted from Microsoft Excel 2013 Power Programming with VBA by John Walkenbach, pages 956-957.
That being said, I tested your issue above by recording a relatively complex macro that creates a new sheet, performs 25 automatic actions, and shows a print preview, then closes the print preview when I click OK on a MsgBox. I iterated the program to do this 1000 times. I never had an issue with the PrintPreview.
Its kind of a shot in the dark, but I tried cleaning up your code a little bit. See if that has any affect at all.
Sub Test()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim isWS As Boolean
On Error GoTo sub_err
Set wb1 = Workbooks.Open(Filename)
Set wb2 = ThisWorkbook
For i = LBound(sArray) To UBound(sArray) 'Loops through array, copies available data from last report
ShtName = sArray(i, 0)
isWS = True
Set ws1 = wb1.Sheets(ShtName)
If isWS Then
Set ws2 = wb2.Sheets(ShtName)
ws1.Columns("A:U").Copy
ws2.Columns("BE:BV").PasteSpecial xlPasteValues
Application.CutCopyMode = False
lastrow = ws2.Cells(ws2.Rows.Count, "BE").End(xlUp).Row
ws2.Range("BA2:BC2").AutoFill Destination:=ws2.Range(ws2.Cells(2, "BA"), ws2.Cells(lastrow, "BC")), Type:=xlFillDefault
End If
DoEvents
Next i
wb1.Close False
ws2.Activate
sub_exit:
Exit Sub
sub_err:
If Err.Number = 9 Then
isWS = False
Resume Next
Else
MsgBox "Error: " & Err.Number & vbNewLine & Err.Description
Resume sub_exit
End If
End Sub
The big thing I did was split off your error handling, this way it only resumes next when you don't find the sheet in wb1 and all other errors (none error 9 errors) will still show your error message.
I also made sure all your Range(), Cells(), and Columns() were referencing the correct sheet (just incase excel is getting confused) and I assumed the active sheet was the correct sheet, but you may want to verify this in the code to make sure it's doing the correct thing in the correct sheet.
Now the copy code only runs if isWS = true, and it will always be true unless an error 9 code is thrown. The only thing I didn't know was if wb2 would always have a sheet name equal to ShtName but I'm guessing it will since it's inside your if err.number = 0
I'm not sure if this will change anything for you, but I'd be curious to hear your results other way.
I am new to VBA. I have written a code to delete a particular sheet. After execution of that delete sheet macro, excel macro stopped execution. It didnt execute futher..
Here is my code..
Sub CopyAcross()
Dim sheetName As String
sheetName = "Master_Base"
If WorksheetExists(sheetName) Then
DeleteSheet (sheetName)
End If
MsgBox "Debug"
Workbooks("Master_Base.csv").Sheets("Master_Base").Copy Before:=Workbooks("Copy of test.xlsm").Worksheets("Sheet3")
End Sub
Sub DeleteSheet(strSheetName As String)
' deletes a sheet named strSheetName in the active workbook
Application.DisplayAlerts = False
Sheets(strSheetName).Delete
Application.DisplayAlerts = True
End Sub
Can any one help on this,
Thanks in advance.
I was experiencing the same issue, on a Windows 7 computer with Excel version 16.0.10730.20264 32-bit, the code ran fine without issue. However, on a Windows 10 computer with the same Excel install version, the macro would immediately stop execution following the Sheets.Delete line.
I found that this was only happening where I was attempting to manipulate a workbook that contained VBA code, that I had opened during the macro.
The issue is caused by the macro security settings on the computer. If you set Automation Security to Low before opening the workbook, you should no longer get the error:
Use the code:
Application.AutomationSecurity = msoAutomationSecurityLow
Since you are working with multiple workbooks, use objects. Else your code MAY work with the wrong workbook/worksheet
Try this (UNTESTED)
Sub CopyAcross()
Dim wbI As Workbook, wbO As Workbook
'~~> The workbook from where the code is running
Set wbO = ThisWorkbook
'~~> Here you open the csv
Set wbI = Workbooks.Open("E:\OPM\OPM Sheet\Master_Base.csv")
'~~> This will delete the sheet if it exists
'~~> no need to check if it exists
On Error Resume Next
Application.DisplayAlerts = False
wbO.Sheets("Master_Base").Delete
Application.DisplayAlerts = True
On Error GoTo 0
'~~> The csv will always have 1 sheet
'~~> so no need providing a name
wbI.Sheets(1).Copy Before:=wbO.Worksheets("Sheet3")
End Sub