Updating linked Excel charts in PowerPoint - excel

I can do this by going to file -> links -> update automatically.
However, this leads to crashes on other people's computers. This might be a compatibility issue since I'm on PowerPoint 2016.
I have 30+ charts that I would like updated.
I tried the following VBA but I end up with a pop-up from Excel saying:
Microsoft Excel is waiting for another application to complete an OLE action.
This continued popping up for 15 minutes.
Sub ChangeChartData()
Dim pptChart As Chart
Dim pptChartData As ChartData
Dim pptWorkbook As Object
Dim sld As Slide
Dim shp As Shape
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
Set pptChart = shp.Chart
Set pptChartData = pptChart.ChartData
pptChartData.Activate
Set pptWorkbook = pptChartData.Workbook
On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
On Error GoTo 0
pptWorkbook.Close True
End If
Next
Next
Set pptWorkbook = Nothing
Set pptChartData = Nothing
Set pptChart = Nothing
End Sub

One thing I would be interested in trying is, change this:
On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
On Error GoTo 0
To this. (I had inquired earlier what happens if you remove On Error Resume Next and you never answered. As a general rule, you should never do that unless you are absolutely certain you know what you're doing with that, and are handling potential errors adequately, in this case you probably are not, but I don't know if there are errors).
'On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
'On Error GoTo 0
If that raises an error, please inform what specific error message is given.
If that doesn't raise an error, but results in the same problems, try this instead:
'On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
'On Error GoTo 0
Exit For '### Get out of the loop
If, as you say, all charts are linked to same data, then I think it's safe to Exit For to avoid redundant calls that aren't doing anything. Still problem, try:
'On Error Resume Next
'update first link
pptWorkbook.RefreshAll
'On Error GoTo 0
Exit For '### Get out of the loop
Finally, and I have some suspicion that this might actually work, try this. You've said there are no formula reference or external links (database, text, web queries, etc.) so it shouldn't seem necessary to do a RefreshAll or even UpdateLink seems pointless. Simply activating the chartdata worksheet and then immediately closing it seems like it should update the data in the PPT embedded Excel sheet and Chart. At least it does when I test on a non-Bloomberg file.
pptChartData.Activate
Set pptWorkbook = pptChartData.Workbook
'On Error Resume Next
'update first link
'pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
'On Error GoTo 0
pptWorkbook.Close True
Exit For
Note: again you want the Exit For because there is no sense in refreshing the same file 30 times.

For me worked this:
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
shp.Chart.ChartData.Activate
shp.Chart.ChartData.Workbook.UpdateLink shp.Chart.ChartData.Workbook.LinkSources(1)
shp.Chart.ChartData.Workbook.Close True
End If
Next
Next
i.e. not creating new variables just working with the original ones.

I've just come across this happen today on a colleagues laptop. To solve the issue, in Excel go to File>Trust Center>Trust Center Settings>External Content select top radio button Enable all Data Connections

Related

VBA Excel select & delete all shapes with the same ID and remove

I would like to delete all the shapes from my sheet. They have the same ID.
I found two codes:
The first one:
Public Sub ActiveShapes()
Dim ShpObject As Variant
If TypeName(Application.Selection) = "Firestop" Then
Set ShpObject = Application.Selection
ShpObject.Delete
Else
Exit Sub
End If
End Sub
is not working. There are no errors, but also no reaction at all.
The second one:
Selecting a shape in Excel with VBA
Sub Firestopshapes()
ActiveSheet.Shapes("Firestop").Delete
End Sub
works, but remove only one by one element. In my event, all the elements have the "Firestop" ID. I would like to make them all deleted at once. How can I do that?
The issue is thet If TypeName(Application.Selection) = "Firestop" Then is never true. Have a look into the TypeName function does not return the name of the Application.Selection but instead it returs what type Application.Selection is. Here it probably returns Object because a shape is an object.
Actually names are unique. You cannot add 2 shapes with the same name. That is why ActiveSheet.Shapes("Firestop").Delete only deletes one shape.
There seems to be a bug that when you copy a named shape 2 shapes with the same name exist (which should not be possible). You can get around this by deleting that shape in a loop until you get an error (no shape with that name is left).
On Error Resume Next
Do
ActiveSheet.Shapes("Firestop").Delete
If Err.Number <> 0 Then Exit Do
Loop
On Error GoTo 0 'don't forget this statement after the loop
It is not recommended to use On Error Resume Next often. We recommend using it only when it is indispensable.
Sub test()
Dim shp As Shape
Dim Ws As Worksheet
Set Ws = ActiveSheet
For Each shp In Ws.Shapes
If shp.Name = "Firestop" Then
shp.Delete
End If
Next shp
End Sub

Run-time error '1004': Microsoft Excel cannot paste the data

I have looked up the question and have seen several solutions addressing things like Select or having protected worksheets, none of which apply to me here.
For various reasons, I can't post the entire code, but I will give a description of what it does and post the exact sub that is giving me issues.
I have a Macro that generates a number of worksheets based on the Month and Year input by the user (so "1" - "31" or "1" - "30" etc). To generate these worksheets, the macro makes copies of a worksheet fittingly named "EXAMPLE". One thing that is copied is a picture (just a rectangle with the word 'Export' on it) that has a macro attached to it.
I recently made what I thought was a cosmetic change by moving the location of this picture, since then, when I run the macro I get an error:
"Run-time error '1004':
Microsoft Excel cannot paste the data."
And options for 'End' 'Debug' and 'Help'
If I select 'Debug' it points me to a second macro which is called during the process of the generation macro'
Sub CopyAllShapes()
Dim ws As Worksheet
' Sets the non-generated worksheets as an array
nSheets = Array("EXAMPLE", "Weekly Totals", "Menu")
' Copies the Picture from the EXAMPLE sheet to all worksheets not in the array and then assigns a
' seperate Macro called "Export" to the picture on each of these sheets.
For Each ws In ActiveWorkbook.Worksheets
If Not IsNumeric(Application.Match(ws.Name, nSheets,0)) Then
Sheets("EXAMPLE").Shapes("Picture 1").Copy
ws.Range("J62").PasteSpecial
ws.Shapes("Picture 1").OnAction = "Export"
End If
Next ws
Application.CutCopyMode = xlCopy
End Sub
The Debug option highlights the line
ws.Range("J62").PasteSpecial
What really confuses me is that if I select 'End' instead of 'Debug', the macro stops, but all the the sheets have had the picture pasted as well as the Export Macro assigned and everything works as expected. If I were the only person using this, it would be a minor annoyance, but this document is used by many people that can't reliable be told to "just ignore" the error. Since the macro is functioning as expected, how can i troubleshoot what is causing the problem and make the error go away?
As I said, I can't post the entire macro, but I can post some bits and pieces if anyone needs more info.
Not a pure fix, but this code will retry the Copy/Paste if it fails (up to 3 times), instead of just dropping it:
Const MaxRetries AS Long = 3
Sub CopyAllShapes()
Dim ws As Worksheet
Dim TimesRetried As Long
' Sets the non-generated worksheets as an array
nSheets = Array("EXAMPLE", "Weekly Totals", "Menu")
' Copies the Picture from the EXAMPLE sheet to all worksheets not in the array and then assigns a
' seperate Macro called "Export" to the picture on each of these sheets.
For Each ws In ActiveWorkbook.Worksheets
If Not IsNumeric(Application.Match(ws.Name, nSheets,0)) Then
TimesRetried = 0
CopyExampleShape:
On Error Resume Next
Sheets("EXAMPLE").Shapes("Picture 1").Copy
ws.Range("J62").PasteSpecial
'If the Copy/Paste fails, retry
If Err Then
On Error GoTo -1 'Clear the Error
'Don't get stuck in an infinite loop
If TimesRetried < MaxRetries Then
'Retry the Copy/paste
TimesRetried = TimesRetried + 1
DoEvents
GoTo CopyExampleShape
End If
End If
On Error GoTo 0
ws.Shapes("Picture 1").OnAction = "Export"
End If
Next ws
Application.CutCopyMode = xlCopy
End Sub
I have come across a similar issue before, and it was been down to another program (in one case Skype) reacting to data being added to the Clipboard by "inspecting" it. That then briefly locked the clipboard, so the Paste/PasteSpecial operation failed. This then caused the Clipboard to be wiped clean... All without Excel doing anything wrong.
"It is possible to commit no mistakes and still lose. That is not a weakness; that is life." ~ Jean-Luc Picard
On moving to Office 365 and Win10 (can't say which of those was the culprit) I found a bunch of existing macros which would give that same error when trying to paste a copied image onto a worksheet.
When entering debug, the "paste" line would be highlighted, but if I hit "Continue" it would (after one or two attempts) run with no errors.
I ended up doing this:
'paste problem fix
Sub PastePicRetry(rng As Range)
Dim i As Long
Do While i < 20
On Error Resume Next
rng.PasteSpecial
If Err.Number <> 0 Then
Debug.Print "Paste failed", i
DoEvents
i = i + 1
Else
Exit Do
End If
On Error GoTo 0
i = i + 1
Loop
End Sub
...which looks like overkill but was the only reliable fix for the problem.
EDIT: cleaned up and refactored into a standalone sub.
Just wanted to let everyone know I have found a (sort of) solution. Based on the answers/comments from Tim Williams and PeterT I modified the code to look like this:
Sub CopyAllShapes()
Dim ws As Worksheet
' Sets the non-generated worksheets as an array
nSheets = Array("EXAMPLE", "Weekly Totals", "Menu")
' Copies the Picture from the EXAMPLE sheet to all worksheets not in the array and then assigns a
' seperate Macro called "Export" to the picture on each of these sheets.
For Each ws In ActiveWorkbook.Worksheets
If Not IsNumeric(Application.Match(ws.Name, nSheets,0)) Then
Sheets("EXAMPLE").Shapes("Picture 1").Copy
On Error Resume Next
ws.Range("J62").PasteSpecial
On Error Goto 0
ws.Shapes("Picture 1").OnAction = "Export"
End If
Next ws
Application.CutCopyMode = xlCopy
End Sub
This has successfully ignored the error and everything is working properly now! Thanks everyone for your help, hopefully this aids someone else in the future!

ErrorHandler not working -> compiling error / object not found

I've created an excelfile with a couple of activeX-elements. So far the file is working as intended, now I'm working around "user errors" which might occur during daily business.
For later use the activeX-elements (Toggle-, Command- & SpinButtons) are quite important, so I've created some subs to restore each element. They are working as expected.
The issue: now what if someone deletes a button? I tried working with If-Statements (if >element< is nothing then...), but it didn't work. The next approach was "On Error GoTo".
So I've built an ErrorHandler and as a regular code it works as intended. The handler creates an SpinButton with the desired name. If I build an error into my code (a = 1/0) the handler is doing his job, but that's just 'in vitro'.
In vivo, if my desired >element< is not there, my code ends in an error (compiling error_ method or object not found), but my handler does nothing, despite this is its sole purpose.
My code:
Sub Cal_SpinButton_Nr()
Subroutine:
On Error GoTo CreateObject:
With Tabelle5.SpinButton_Nr
.Left = 198
.Height = 65.25
.Top = 1.5
.Width = 54.75
.Orientation = fmOrientationVertical
.BackColor = &H8000000F
.ForeColor = &H80000012
End With
Exit Sub
CreateObject:
Tabelle5.OLEObjects.Add("Forms.SpinButton.1").Name = "SpinButton_Nr2"
'Resume Subroutine
End Sub
Option Explicit is on and the sub contains no variables. As long as there is an object (SpinButton_Nr) it's working. With no object I get a compiling error.
"Resume Subroutine" is silenced right now to avoid an endless loop (I learned my lesson by silencing "exit sub" first and pressing F5...), regular function is to fire the same sub again in order to put the new object in the right place.
For ('in vitro') testing reasons the new object is called _Nr2, later it's just _Nr.
Now the question: Why is a compiling error not covered by the "on error"-statement? How can the code be modified in order to work properly?
Syntax should be "If >element< exists, set properties, if <element> is not there then create it and set properties afterwards.
I suggest the following:
So you actually test if a spin button exists if not create it before you start formatting. No odd error handling and goto jumps in your procedure.
Option Explicit
Sub Cal_SpinButton_Nr()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Tabelle5") '<-- tab name
'OR
'Set ws = Tabelle5 '<-- VBA code name
Dim SpinBtn As Object
On Error Resume Next 'next line errors if no spinbutton exists
Set SpinBtn = ws.OLEObjects("SpinButton_Nr")
On Error Goto 0 'always re-activate error reporting!
'if no spinbutton is found create it before formatting starts
If SpinBtn Is Nothing Then
Set SpinBtn = ws.OLEObjects.Add("Forms.SpinButton.1")
SpinBtn.Name = "SpinButton_Nr"
End If
'format spin button
With SpinBtn
.Left = 198
.Height = 65.25
.Top = 1.5
.Width = 54.75
'not that for these .Object is necessary because of using .OLEObjects("SpinButton_Nr")
.Object.Orientation = fmOrientationVertical
.Object.BackColor = &H8000000F
.Object.ForeColor = &H80000012
End With
End Sub
Compiling errors are errors, which occur in compiling time. They cannot be covered by OnError statement. The errors, covered by OnError are run-time errors, which appear after compiling and during run time.
"As long as there is an object (SpinButton_Nr) it's working. With no object I get a compiling error." - If you want to get run-time error and not compiling error, the binding of the object should be done during run-time.
Imagine the following scenario, with Tabelle5 as a worksheet variable in Excel:
Sub CompileTimeError()
With Tabelle5
Debug.Print .Cells(1, 1)
End With
End Sub
This would not compile, because VBE would not find the Tabelle5 object. However, if you want a run-time error, this is a possible solution:
Sub RunTimeError()
Dim nameOfWorksheet As String
nameOfWorksheet = "Tabelle5"
With Worksheets(nameOfWorksheet)
Debug.Print .Cells(1, 1)
End With
End Sub

VBA Excel simple Error Handling

I have looked online as much as I could (except for the Microsoft support website, which is blocked at work for some reason). I am trying to simply skip an error. My code written here is simplified but should work the same way.
What my code is supposed to do:
One of my subs creates shapes in a loop and names them (btn_1, btn_2, etc). But before creating them, it calls a sub that tries to delete them so as not to create duplicates. This sub loops through (btn_1, btn_2, etc) and deletes the shapes using:
for i = 1 to (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
shp.delete
next
Of course, it happens that the shape cannot be deleted because it simply does not exist. I have found that most of the time, the reccomended fix is to add (on error resume next) before setting the shape, as I get an error saying it does not exist. I have tried it inside the loop, before the loop, etc, like so:
for i = 1 to (a certain number)
On Error Resume Next
Set shp = f_overview.Shapes("btn_" & i)
shp.delete
next
As far as I understand it is supposed to loop right through if the shape doesn't exist, but I still get the same error whether or not I add the On error resume next! What am I doing wrong?
EDIT: There is no error when the shapes do exist.
I have found that most of the time, the reccomended fix is to add (on error resume next) before setting the shape, as I get an error saying it does not exist.
NO!
The recommended way to handle runtime errors is not to shove them under the carpet and continue execution as if nothing happened - which is exactly what On Error Resume Next does.
The simplest way to avoid runtime errors is to check for error conditions, and avoid executing code that results in 100% failure rate, like trying to run a method on an object reference that's Nothing:
For i = 1 To (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
If Not shp Is Nothing Then shp.Delete
Next
In cases where you can't check for error conditions and must handle errors, the recommended way is to handle them:
Private Sub DoSomething()
On Error GoTo CleanFail
'...code...
CleanExit:
'cleanup code here
Exit Sub
CleanFail:
If Err.Number = 9 Then 'subscript out of range
Err.Clear
Resume Next
Else
MsgBox Err.Description
Resume CleanExit
End If
End Sub
There is nothing WRONG in using OERN (On Error Resume Next) provided you understand what you are doing and how it is going to affect your code.
In your case it is perfectly normal to use OERN
Dim shp As Shape
For i = 1 To (a certain number)
On Error Resume Next
Set shp = f_overview.Shapes("btn_" & i)
shp.Delete
On Error GoTo 0
Next
At the same time ensure that you don't do something like
On Error Resume Next
<Your Entire Procedure>
On Error GoTo 0
This will suppress ALL errors. Use proper error handling as shown by Matt
Edit:
Here is another beautiful example on how to use OERN This function checks if a particular worksheet exists or not.
Function DoesWSExist(wsName As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Sheets(wsName)
On Error GoTo 0
If Not ws Is Nothing Then DoesWSExist = True
End Function
If you wish you can also loop through all the sheets to check is the sheet exists or not!
Instead of trying to blindly delete shapes and skipping errors, why not run through the list of known shapes and delete them. Then you don't have to worry about an On Error Resume Next which often ends up being abused.
Sub Test(TheSheet As Worksheet)
Dim Shp as Shape
For Each Shp in TheSheet.Shapes
If left(Shp.Name, 4) = "btn_" Then
Shp.Delete
End if
Next
End Sub
If you want to delete all shapes, remove the If statement. If you want to delete a number of differently named shapes, modify the If statement appropriately.
It sounds like you have the wrong error trapping option set. Within the VBA Editor, Select Tools -> Options. In the window that opens, select the General tab, and pick the Break on Unhandled Errors radio button. This should allow Excel to properly process the On Error Resume Next command.
I suspect that you have Break on All Errors selected.
Try:
On Error Resume Next
for i = 1 to (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
if err<>0 then err.clear else shp.delete
next
on Error Goto 0

On Error Resume Next seemingly not working

I have the following two lines of code:
On Error Resume Next
myWorkbook.Sheets("x").Columns("D:T").AutoFit
I've stepped into the macro and executed the line On Error Resume Next and then on the next line myWorkbook... it does the following:
Why doesn't the compiler resume the next line of code?
On Error has been liberally used throughout the procedures code; I realize best practice is to use this as little as possible but it seems to fit the purpose of this macro.
Reading this SO QUESTION it says that you can't have one set of error trapping within another. How can I guarantee that one set of error trapping has been "closed off" before the code moves on - does On Error Goto 0 reset the error trapping? If it does reset then why doesn't resume work in the following?:
Sub GetAction()
Dim WB As Workbook
Set WB = ThisWorkbook
On Error GoTo endbit:
'raise an error
Err.Raise 69
Exit Sub
endbit:
On Error GoTo 0
On Error Resume Next
WB.Sheets("x").Columns("D:T").AutoFit
End Sub
There is also a VBA setting that will cause On Error ... statements to be ignored and that dialog box to always appear. See this answer for more details on checking/changing the option: https://stackoverflow.com/a/3440789/381588
An example of an error when the initial error is not closed out.
Sub GetAction()
Dim WB As Workbook
Set WB = ThisWorkbook
On Error GoTo endbit:
'raise an error
Err.Raise 69
Exit Sub
endbit:
On Error Resume Next
WB.Sheets("x").Columns("D:T").AutoFit
End Sub
As you have found, within the same function or subroutine, On Error Resume Next doesn't override On Error Goto ... if it's still active.
You are correct that On Error Goto 0 restores the default error handler.
There are some cases where On Error is the most appropriate way to handle an exceptional condition. I prefer to use the following structure:
On Error Resume Next
statement which might fail
On Error Goto 0
if statement has failed then ...
This keeps everything together, but in other cases a generic error handler at the end of the procedure can be better.
I've found that in functions/subs that iterates over nested objects, errorhandling might be a drag in VBA. A solution that works for me to better handle complex iterations is separating setting of objects in their own functions, e.g.
main function/sub:
set FSOfolder = SetFSOFolder(FSOobject, strFolder)
Private Function SetFSOFolder(FSO as scripting.FileSystemObject, strFolder as string) as Scripting.Folder
on error resume Next
set SetFSOFolder = FSO.GetFolder(strFolder)
on error goto 0
End Function
and then in the next line in main function:
if (not fsofolder is nothing) then
I agree using On Error Resume Next is not best practice but most/many of my Access apps are not overly sensitive to minor nuances in data integrity (i.e. analytical or reporting, not transactions and not audited). For this reason I use OERN quite often because VBA is very sensitive to some situations that you cannot anticipate entirely.
1 - will the error cause data corruption. If yes handle it. Many routines I use are just crunching a large volume of records and there may be errors in imported data that haven't been fixed. Usually I have a lot of conversion processes that will let the system clean up its own data eventually.
2 - is the error frequent and non-critical (ie not a key). If yes it's OERN otherwise the error may not be predicable and you end up crashing out or have to write a bunch of I-T-E or Select Case logic to trap it.
Don't use On Error Resume Next instead write code that shouldn't crash.
Note: I am being careful how I phrase that because you never guaranty code doesn't crash. But if you use On Error Resume Next then part of the natural flow of your code is for it to crash, which is wrong, big time wrong.

Resources