Inconsistent error when moving worksheet - vba

The code below is a function to move a worksheet in a workbook. It gets called from a userform that contains a listbox that lists the worksheets in the workbook. The input is an integer that gives the direction which to move the sheet. Left/right in workbook is up/down in the userform listbox, and the userform has up and down buttons that calls the function with different input (+1 for moving right and -2 for moving left).
This function sometimes gives the error Method 'Move' if object '_Worksheeet' failed, but not consistently. Mostly the error comes when moving a sheet a second time, so that I am not able to move a sheet more than once. Once the error occurs I am not able to move the sheet again. I can, however, select a different sheet and move this once before the same thing occurs.
If I implement a message box in the error handling, the behaviour changes. After closing the message box upon error, I can proceeed to move the same sheet again after an error. With a message box I can therefore move a sheet as many times as I want, but it only moves on every other button press. I guess the message box breaks code execution, which for some reason makes the function work again even one the same sheet that gave the error. I have tried replacing the message box with a delay or a selfclosing infobox but this does not give the same result.
To further complicate matters, sometimes when I open the userform application, the move buttons work perfectly without any error. I think this happens when the worksheet is already open before the application is opened.
It all seems very inconsistent, and beyond my knowledge. Any help or suggestions much appreciated.
Function FlyttMåling(Retning As Integer) As Boolean
Application.EnableEvents = False
Application.ScreenUpdating = False
'code to reference the correct workbook based on outside parameters
Dim wb As Workbook, ws As Worksheet
FlyttMåling = True
If Hovedvindu.LuftlydKnapp.Value = True Then
Set wb = ÅpneBok(1)
ElseIf Hovedvindu.TrinnlydKnapp.Value = True Then
Set wb = ÅpneBok(2)
End If
'sets variable to the index of sheet to be moved, chosen from list in userform
Dim nummer As Integer
Set ws = wb.Sheets(1)
If Hovedvindu.MålingerFrame.Liste.ListIndex < 0 Then
Exit Function
Else
Set ws = wb.Sheets(Hovedvindu.MålingerFrame.Liste.Value)
End If
nummer = ws.Index
'exit function if trying to move first sheet to the left or last sheet to the right
If (Retning = 1 And nummer = wb.Sheets.count) Or (Retning = -2 And nummer = 2) Then
Exit Function
End If
'code that moves worksheet
ws.Activate
On Error GoTo errHandler:
errResume:
ws.Move after:=wb.Sheets(nummer + Retning) 'THIS LINE CAUSES ERROR
On Error GoTo 0
Call oppdaterListe
'reselect the moved worksheet in the userform list
For i = 0 To Hovedvindu.MålingerFrame.Liste.ListCount - 1
If ws.Name = Hovedvindu.MålingerFrame.Liste.List(i) Then
Hovedvindu.MålingerFrame.Liste.Selected(i) = True
Exit For
End If
Next i
Application.EnableEvents = True
Application.ScreenUpdating = True
Exit Function
'error handling just sets the return to false to notify failure to move sheet
errHandler:
FlyttMåling = False
End Function

Found a workaround for this problmem. Changing the wb.move to a wb.copy, and then deleting the old sheet and renaming the copy to the name of the original sheet makes this code work as intended.

Related

Excel VBA Retrieve Constant from Worksheet Code Fails When New Code is Written

In an attempt to retrieve constants from each worksheet in some reporting workbooks I use, three years ago I wrote some code that gets included in each worksheet. Here's an example of the code:
Option Explicit
' Determine the type of worksheet
Private Const shtType As String = "CR"
Public Function GetShtType()
GetShtType = shtType
End Function
In other code that gets the values from the worksheets for processing, the following section of code is used, where 'wksToCheck' is the worksheet in question. This code is stored in a personal macro workbook, not in the workbook with the worksheet code:
' Get the sheet 'type' if it has one
On Error Resume Next
shtType = wksToCheck.GetShtType()
If Err.Number <> 0 Then
' We do not have a type
shtType = "Unknown"
Err.Clear
End If ' Err.Number...
On Error GoTo Error_BuildTemplateWbk
My problem is, I use the code above to process workbooks several times a week, and I have for the past three years. Now, I am trying to write some new code with the above block to process the report workbooks in a different way. However, when I run code with the above block now, I get a 'Method or Data Member Not Found' error on the '.GetShtType()' portion of the code. I cannot compile the code and of course, consequently, the code doesn't work. I have tried adding the worksheet code to a worksheet in the macro workbook to see if that would fix the problem. It hasn't. Does anyone have any ideas? I am running Excel 2013 on a Windows 7 PC. Any ideas?
Brian
Using late-binding, should avoid the error, Dim wksToCheck As Object, but you'll lose the intellisense.
If you're open to alternatives, you may have better luck simply using the CallByName function, or using worksheet's CustomProperties.
Using CallByName preserves backwards compatibility with your older workbooks if needed:
shtType = CallByName(wksToCheck, "GetShtType", VbMethod)
Or, using CustomProperties instead of a custom method, in your worksheets:
Private Sub Worksheet_Activate()
Const PropName$ = "ShtType"
Const ShtType$ = "CR"
On Error Resume Next
Me.CustomProperties(PropName) = ShtType$
If Err.Number = 13 Then
Me.CustomProperties.Add "PropName", ShtType
End If
End Sub
Then,
' Get the sheet 'type' if it has one
On Error Resume Next
shtType = wksToCheck.CustomProperties("ShtType")
If Err.Number = 13 Then
' We do not have a type
shtType = "Unknown"
Err.Clear
End If ' Err.Number...
On Error GoTo Error_BuildTemplateWbk

Data Validation and Worksheet Change Event

I use a VBA macro to query a database and build a list of available projects when a workbook is opened using the workbook activate event. I have project numbers and project names that are combined into two separate data validation lists and applied to two cells. The worksheet change event tests for changes in these cells, splits their data validation lists into arrays, and chooses the corresponding project information from the other array. For instance, if I pick a project number, the worksheet change event finds the project number's position in the project number array, and then picks the project's name from the name array based on position.
This works perfectly whenever a value is selected from the drop down, but I run into problems when values outside the list are entered. For instance, if I enter a blank cell I may get the data validation error or I may get a type mismatch when I use match to find the entered value in the array. I have an error handler to handle the type mismatch, but I would like the data validation error to trigger every time instead. Another problem is that Events will sometimes be disabled. This is much more serious because users will not have a way to turn these back on.
On top of this, I cannot figure out where or how this is happening. I can't replicate how the Events are disabled using breaks because duplicating the steps that lead to the events being disabled with breaks in place only leads to my error handler. However, when breaks aren't applied, the error handler will sometimes fail to trigger and the events will be disabled. Since I'm disabling events just before I parse arrays, I'm thinking the worksheet change fails at the Loc=Application.Match(Target.Text, NumArr, 0) - 1 line, but I can't figure out why no error would be triggered. At the very least, I should get a message with the error number and description, and events should be re-enabled.
Can anyone advise on the interaction between worksheet change and data validation? What is the call order here? Any other advice? Anything I'm missing?
ETA: I've Googled this, but I haven't found anything that helps. Everything that comes up is about working the data validation into worksheet change, nothing about the interaction or call order.
ETA #2: After trying the experiment in the answer below (Thanks Gary's Student), this gets a little more odd. If I choose "Retry" and choose the old, default value, I get the old value three times. If I hit delete, I get a space in the message box, but only one message box. Then the cell is left blank. I can put DV into a loop by clicking "Retry" and accepting the space. The DV error will come up until I click cancel. Then I will get a series of empty text message boxes, one for each time I retried the empty cell. If I start off with a listed value, clear the cell with backspace, click "Retry," and try to select another value, the worksheet change event fails at Intersect 3 times. I think the answer below sheds more light on what is going on, but it does bring up more questions also.
Here is the code I have:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NumArr() As String
Dim ProjArr() As String
Dim Loc As Integer
On Error GoTo ErrHandler:
If Target.Address = "$E$4" Then
'Disable events to prevent worksheet change trigger on cell upates
Application.EnableEvents = False
'Parse validation lists to arrays
NumArr = Split(Target.Validation.Formula1, ",")
ProjArr = Split(Target.Offset(1, 0).Validation.Formula1, ",")
'Change error handler
On Error GoTo SpaceHandler:
'Determine project number location in array
Loc = Application.Match(Target.Text, NumArr, 0) - 1
'Change error handler
On Error GoTo ErrHandler:
'Change cell value to corresponding project name based on array location
Target.Offset(1, 0) = ProjArr(Loc)
'Unlock cells to prepare for editing, reset any previously imported codes
Range("C8:G32").Locked = False
'Run revenue code import
RevenueCodeCollector.ImportRevenueCodes
'Re-enable events
Application.EnableEvents = True
End If
If Target.Address = "$E$5" Then
Application.EnableEvents = False
NumArr = Split(Target.Validation.Formula1, ",")
ProjArr = Split(Target.Offset(-1, 0).Validation.Formula1, ",")
Loc = Application.Match(Target.Text, NumArr, 0) - 1
Target.Offset(-1, 0) = ProjArr(Loc)
Range("C8:G32").Locked = False
RevenueCodeCollector.ImportRevenueCodes
Application.EnableEvents = True
End If
Exit Sub
ErrHandler:
MsgBox Err.Number & " " & Err.Description
Application.EnableEvents = True
Exit Sub
SpaceHandler:
MsgBox "Pick a project from the dropdown.", vbOKOnly, "Error"
Application.EnableEvents = True
End Sub
You have a very open-ended question...........not having the time to do a full whitepaper, here is a simple experiment. I use the Event code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A1 As Range, rINT As Range
Set A1 = Range("A1")
Set rINT = Intersect(A1, Target)
If rINT Is Nothing Then Exit Sub
MsgBox A1.Value
End Sub
and in A1, I setup DV as follows:
If I use the drop-down, I get the value entered and I also get the MsgBox. However, if I click on the cell and type some junk what happens is:
the DV alert occurs and I touch the CANCEL Button
I get 2 MsgBox occurrences, each with the original contents rather than the attempted junk !!
I have absolutely no idea why the event is raised since the cell is not actually changed, let alone why the Event is raised twice !! It is almost as if
the event is raised on junk entry, but the DV alarm has precedence, the DV reverse the entry and another event is raised, and finally both events get processed.
Hopefully a person smarter than me will chime in.
With ref to the query, Workaround for the DV and change event is managed.
Public strRange As String
Public bCheck As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If bCheck Then Exit Sub
MsgBox "Correct Entry!"
strRange = Target.Address
bCheck = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Address <> strRange Then bCheck = False
End Sub
http://forum.chandoo.org/threads/multiple-worksheet-change-event-with-data-validation.32750

Why does assigning a reference in my spreadsheet sometimes work and sometimes not?

I have a few cells in my excel workbook which are available for a client to put his own values. I wanted the workbook to initialize those cells with default values. In order to do so I have a worksheet "Arkusz do makr", where I store the values.
In a module "GM" I declare a variable to reference my worksheet easier like this:
Public M As Worksheet
Then I initialize this variable and set my default values like this (in ThisWorkbook):
Private Sub Workbook_Open()
Set M = Worksheets("Arkusz do makr")
Worksheets("Values").Range("Value1") = M.Range("Value1")
Worksheets("Values").Range("Value2") = M.Range("Value2")
Worksheets("Values").Range("Value3") = M.Range("Value3") `etc
End Sub
Now sometimes this works like a charm, and sometimes, when I open the workbook I get a
Run-time error '91': Object variable or With block variable not set.
Could someone please explain this behaviour to me? Additionally I would like to ask if my approach makes sense, since I have a hard time grasping the order of events in excel as well as the range of its objects.
EDIT: Additionally I should mention that the Debug function highlights the first Worksheets... line in my code. In specific worksheets I reference the M object as well, though I thought it changes anything here...
Try to change the code of this Sub like below.
I have added a simple error handling - if there is no worksheet "Arkusze do makr" or "Values" in your workbook, warning message is displayed and default values are not copied.
You can find more comments in code.
Private Sub Workbook_Open()
Dim macrosSheet As Excel.Worksheet
Dim valuesSheet As Excel.Worksheet
'------------------------------------------------------------------
With ThisWorkbook
'This command is added to prevent VBA from throwing
'error if worksheet is not found. In such case variable
'will have Nothing as its value. Later on, we check
'the values assigned to those variables and only if both
'of them are different than Nothing the code will continue.
On Error Resume Next
Set macrosSheet = .Worksheets("Arkusz do makr")
Set valuesSheet = .Worksheets("Values")
On Error GoTo 0 'Restore default error behaviour.
End With
'Check if sheets [Values] and [Arkusz do makr] have been found.
'If any of them has not been found, a proper error message is shown.
'In such case default values are not set.
If valuesSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Values] not found")
ElseIf macrosSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Arkusz do makr] not found")
Else
'If both sheets are found, default values are copied
'from [Arkusz do makr] to [Values].
'Note that if there is no Range named "Value1" (or "Value2" etc.)
'in any of this worksheet, another error will be thrown.
'You can add error-handling for this case, similarly as above.
With valuesSheet
.Range("Value1") = macrosSheet.Range("Value1")
.Range("Value2") = macrosSheet.Range("Value2")
.Range("Value3") = macrosSheet.Range("Value3")
End With
End If
End Sub

Bypass Old Hyperlink Error

I have the following issue:
I have a macro script for excel which is running through more then 300 excel workbooks,with several sheets.
The problem is that some of this sheets have faulty hyperlinks and each time I run the macro,the pop-up message informing me that the hyperlink found in the sheet is not working and I have to click each time , : ,,cancel'' . Is there a way (code) that I can write that will automatically ,,cancel'' the pop up question ,if it appears?
You should be able to bypass this by wrapping your code in:
Application.DisplayAlerts = False
-- your code --
Application.DisplayAlerts = True
Maybe the privacy options are related?
http://office.microsoft.com/en-001/excel-help/enable-or-disable-security-alerts-about-links-to-and-files-from-suspicious-web-sites-HA010039898.aspx
Let's say your hyperlinks were pointing to a worksheet's cells, and that worksheet may no longer exist, the best thing to do may be to simply remove those hyperlinks.
This is how you'd do this :
Sub RemoveDeadHyperlinks()
For Each hyperL In ActiveSheet.Hyperlinks
'Extract name of the sheet from the subaddress
toSheet = Left(hyperL.SubAddress, InStr(hyperL.SubAddress, "!") - 1)
If WorksheetExists(toSheet) Then
'Most likely a valid hyperlink!
Else
'Most likely a dead one!
hyperL.Delete
End If
Next
End Sub
Public Function WorksheetExists(ByVal WorksheetName As String) As Boolean
On Error Resume Next
WorksheetExists = (Sheets(WorksheetName).Name <> "")
On Error GoTo 0
End Function

Delete Sheets and avoid Excel asking the user to confirm, using custom messages instead

I have a button that triggers a chain of events. One of these events is to delete a sheet. Before the user deletes anything, I pop up my custom YES/NO message asking them to confirm the whole process.
Then comes the sub event of deleting the sheet, and Excel pops up its own window for confirming the removal of the sheet. Problem is that if the user says "no" at that point, that sets my application in an inconsistent state.
How can I bypass Excel asking to confirm the deletion of a sheet ?
You can change the default display alert parameter of Excel using:
Application.DisplayAlerts = False
don't forget to restore the standard behavior at the end of your process:
Application.DisplayAlerts = True
I ran into this issue using Excel 2016, and surprisingly DisplayAlerts was useless. Not sure if anyone else has experienced this. I'm still unsure as to why, but reading this thread, according to the remarks of the Worksheet.Delete method (here):
When you delete a Worksheet , this method displays a dialog box that prompts the user to confirm the deletion. This dialog box is displayed by default. When called on the Worksheet object, the Delete method returns a Boolean value that is False if the user clicked Cancel on the dialog box or True if the user clicked Delete.
In Excel 2016, though Application.DisplayAlerts was set to False, it kept showing the alert after (or rather before) deletion.
I haven't found a true work around yet, so I'm simply making the sheets I want to delete "disappear" using a for each loop:
Sht.UsedRange.clear
For each shp in sht.Shapes
shp.Delete
Next
For each nm in sht.Parent.Names
if nm.RefersToRange.Parent is sht then nm.Delete
Next
sht.visible = xlSheetVeryHidden
(code is an unchecked draft; eventual errors can be treated with an on error resume next mostly)
It's far from ideal, but it does what I need done (at the cost of more memory, sure). Maybe I should turn this reply into a question and see if someone has a better idea for Excel 2016.
TO DELETE ALL SHEETS WITH OUT "REPORT" SHEET **
Dim NM As String
Dim CTS As Integer
Dim CNT2 As Integer
Dim CNT3 As Integer
CNT3 = 1
CNT2 = 1
CTS = Sheets.Count
Do Until CNT2 = CTS + 1
NM = Sheets(CNT3).Name
If Name = "Report" Then
Range("A1").Select
CNT3 = CNT3 + 1
Else
Sheets(NM).Select
Application.DisplayAlerts = False
ActiveWindow.SelectedSheets.Delete
Application.DisplayAlerts = True
End If
CNT2 = CNT2 + 1
Loop