ErrorHandler not working -> compiling error / object not found - excel

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

Related

What could cause the error "Unable to set the Caption property of the CheckBox class"

I occasionally get the following error when running a macro in Excel:
Run-time error '1004': Unable to set the Caption property of the
CheckBox class
The section of code where this occurs looks equivalent this:
Dim ws As Worksheet
Dim cbRange As Range
Dim cb As CheckBox
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets(1)
[...]
With ws
Set cbRange = .Cells(2, 2)
cbRange.Name = "aCell"
Set cb = .CheckBoxes.Add( _
cbRange.Left, _
cbRange.Top, _
cbRange.Width, _
cbRange.Height _
)
End With
With cb
.Caption = "A Checkbox"
.Value = xlOff
.LinkedCell = cbRange.Address
.Name = "aCheckbox"
End With
[...]
Application.ScreenUpdating = True
I am trying to reproduce this issue in a controlled way, but have not succeeded. Hence, I need suggestions of what could be causing this. The only lead I have is that the error seems to occur when I interact with the Excel window during the execution of the macro. The total running time for the macro (the above is just a part of it) is about 5 s.
I do have an extensive test suite that I run quite frequently on this macro and the issue does not occur then. During running I always leave the Excel window alone.
I found that I can reproduce this issue by running the code while the workbook window is minimized. Now I just need to figure out what to do about it, but that's another question!

How can I use If Error GoTo "specific line" multiple times in the same Excel VBA sub?

I have tried using If Error GoTo "blank" multiple times, so that it skips the next section of code if a sheet won't activate. It will work the first time, then the second time it won't skip past the error, just presents it as if there was no If Error command at all. The code looks like this:
On Error GoTo errorHandler1
Windows(MyFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler1:
'bunch of code
On Error GoTo errorHandler2
Windows(MyFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler2:
'bunch of code
If Sheets(secondDetailTab).activate has an error the first time, it will skip to errorHandler1 like it's supposed to, but if there's an error the second time then it just presents the error normally and ignores the On Error GoTo errorHandler2 command. Any suggestions would be greatly appreciated!
You could do it indeed in the way as you want to do it although this is not best practise to do it like that. You need to reset the error with On Error Goto -1 before you "activate" another error handler.
On Error GoTo errorHandler1
Windows(myFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler1:
'bunch of code
On Error GoTo -1
On Error GoTo errorHandler2
Windows(myFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler2:
'bunch of code
Here you find a pretty good tutorial on error handling. There is also an explanation for On Error Goto -1
In the Remarks section of the On Error MS doc
If an error occurs while an error handler is active (between the occurrence of the error and a Resume, Exit Sub, Exit Function, or Exit Property statement), the current procedure's error handler can't handle the error.
My suggestion is that instead of stacking error handlers, you test and avoid errors in the main code. If Sheets(secondDetailTab).Activate is predictably causing errors then first test if the sheet exists and is valid. I wrote a short function that does that:
Function SheetExists(ByRef ExcelBook As Workbook, ByVal SheetName As String) As Boolean
Dim ws As Object
For Each ws In ExcelBook.Sheets
If ws.Name = SheetName Then Exit For
Next ws
SheetExists = Not ws Is Nothing
'True = it Exists, False = Doesn't Exist
End Function
Sub Example()
Const sName As String = "Sheet4"
Dim B As Boolean
B = SheetExists(ThisWorkbook, sName)
MsgBox B
End Sub
You could implement this into your code by changing Sheets(secondDetailTab).Activate into If SheetExists(WB, secondDetailTab) Then Sheets(secondDetailTab).Activate
Error handling in VBA is primitive compared to other languages. It is much better to check for error conditions preemptively than to catch errors after they occur. Check to see if files exist before you try to open them, don't assume the function you called succeeded--check the return value. If you practice these steps consistently, you almost never need to handle errors.
When you do need to handle errors, you can be very specific about the scope of the error trap and which errors you're handling. Here is a pattern I have found myself using a lot. You wrap a single line in the error handler, then check the error code. For example, I prefer Toddleson's code above, which checks to see if a worksheet exists by enumerating the existing worksheets, but this an alternative implementation which illustrates the use of tightly focused error handling.
‘ Get worksheet by name. If the worksheet does not exist, create a new worksheet.
Function GetWorksheet(ByVal name As String) As Worksheet
Dim ws As Worksheet
' Turn on error handling.
On Error GoTo Error_NoSuchSheet
Set ws = Worksheets(name)
' Turn off error handling.
On Error GoTo 0
' Create new worksheet.
If ws Is Nothing Then
Set ws = Worksheets.Add
ws.name = name
End If
Set GetWorksheet = ws
Exit Function
Error_NoSuchSheet:
If Err.Description = "Subscript out of range" Then
' Resume execution on the line following the one that threw the error.
Resume Next
Else
' Invoke the default VBA error handler.
Err.Raise Err.Number
End If
End Function
Note that only a specific error on one specific line is handled. An error on any other line will receive the default error handling (VBA pops up a dialog box). The same thing if any other error occurs on that one line. This pattern provides a much more finely granulated control of error conditions. This is not necessarily the best way to handle an error in every situation and it is far from the only way, but I think it has significant advantages over the pattern you described.
Of course any error handling, or no error handling at all, is significantly better than this.
Function IgnoreAllErors()
' Ignore all errors.
On Error Resume Next
' Your code here
End Function
Please don't do this.

Excel 2013 stops working in sub

I am using a VBA code, that highlights the border color of the active cell as the cell selector moves.
The code is
Private mOutline As Shape
Private Const SelectedShapeName As String = "Selection Box"
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Dim SelectedShape As Shape
Dim SelectedArea As Range
On Error Resume Next
For Each SelectedShape In Sh.Shapes
If SelectedShape.Name = SelectedShapeName Then
SelectedShape.Delete
End If
Next SelectedShape
For Each SelectedArea In Selection.Areas
Set mOutline = ActiveSheet.Shapes.AddShape(msoShapeRectangle, SelectedArea.Left, SelectedArea.Top, SelectedArea.Width, SelectedArea.Height)
With mOutline.OLEFormat.Object.ShapeRange
.Fill.Visible = msoFalse
.Line.ForeColor.RGB = RGB(255, 0, 0)
.Line.Transparency = 0
.Line.Weight = 3
End With
mOutline.Name = SelectedShapeName
Next SelectedArea
On Error GoTo 0 End Sub
When I right-click on a column header in my worksheet, Excel suddenly stops working and exists.
Does anybody know what causes this error, and if so, how I can modify my code to avoid this?
Thank you.
Your code is attempting to add a shape that is too tall for Excel to handle. When you right-click, it is selecting the entire column, so SelectedArea.Top is 0 and SelectedArea.Height is some absurdly large number (15728640 on my machine). That is causing ActiveSheet.Shapes.AddShape to fail.
Remove the On Error Resume Next line entirely. You're ignoring the 1004 error instead of handling it. Then the next line of code attempts to dereference an OLEObject that doesn't exist (which would be the second ignored error), and then attempts to assign a property to the null reference (which would be the third ignored error). I'm not going to test to find out which one is bringing Excel down, but I would guess that either the 2nd or 3rd one is causing an access violation.
Either add proper error handling, or better, avoid the error by testing to see if you have a valid Target:
'Assuming you want to limit to one cell
If Target.Rows.Count > 1 Or Target.Columns.Count > 1 Then Exit 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

1004 error on excel 2013 textbox VBA

I want to run a simple script which I would further enhance. I had something similar working for autofilters yesterday but I misplaced it and am having a hard time to find a solution. I want to use an active x textbox to filter a pivot table. I have tried changing the location of the module as well as using acitveworkbook and activesheet and me but all seem to return a 1004 error. I can confirm the textbox is on the page as well as the pivot table. This is office 13.
Private Sub TextBox1_Change()
ActiveSheet.PivotTables("PivotTable2").PivotFields("Agency").ClearAllFilters
ActiveSheet.PivotTables("PivotTable2").PivotFields("Agency").CurrentPage = ActiveSheet.TextBox1.Text
End Sub
I believe the solution I had yesterday had something to do with activating the textbox. But cant quite rememeber.
Any help would be much appreciated.
Your subroutine must be placed in the Worksheet's code module. I believe the error is happening because the _Change event fires with every keystroke, so it is very likely that the first keystroke creates a TextBox1.Text value which does not correspond with any of the PivotItems in that PivotField.
You could do something like this. Use the _GotFocus event to provide an input prompt. That subroutine will then implicitly raise the TextBox1_Change event by assigning the input value to the TextBox1.Text.
Private Sub TextBox1_GotFocus()
Dim str$
str = InputBox("Please enter a value", "Pivot Field filter")
TextBox1.Text = str
End Sub
I temporarily disable error handling (On Error Resume Next) to assign the filter. Then I check to see if an error happened, and you can (optionally) inform the user that they supplied an invalid criteria.
Sub TextBox1_Change()
Dim pt As PivotTable
Dim fld As PivotField
Set pt = PivotTables("PivotTable2") 'Modify as needed
Set fld = pt.PivotFields("Agency")
With fld
.ClearAllFilters
On Error Resume Next
.CurrentPage = TextBox1.Text
.Orientation = xlPageField
.Position = 1
If Err.Number <> 0 Then
'you may want to include a prompt here, i.e.:
MsgBox "Invalid Filter!", vbInformation
Exit Sub
End If
On Error GoTo 0
End With
End Sub

Resources