I have kind of a paradox problem with my macro enabled workbook (paradox meaning that the very same commands work just fine in another protected sheet of mine):
Once I start protecting my Worksheet, the commands ".Interior.Color" and ".Borders(xlEdgeRight).LineStyle" will continue to produce "application-defined or object-defined errors"
Here's an example of one macro showing that very error:
Private Sub Con1_Click()
Sheet1.Unprotect Password:="bla"
Worksheets("SOLL").Range("N23:S25").ClearContents
Range("N23:S25").Interior.Color = RGB(225, 225, 225)
Range("P23:Q25").Borders(xlEdgeRight).LineStyle = xlNone
Sheet1.Protect Password:="bla"
End Sub
Note: I added the Unprotect/Protect commands to get rid of other errors that kept occuring. Like I said, the two commands work just fine in another sheet, so I really can't figure out any source of the problem. If anyone has encountered similar problems yet or has any ideas for solutions, I'd be glad for the help!
Too long/confusing to put in comments, so I will put this as an answer, perhaps it will shed some light on your problem. You say:
All commands of that Macro refer to the 'ActiveSheet',
But this is not necessarily true. Observe the three different constructs you're using to refer to worksheets:
Explicit reference to a worksheet by its codename: Sheet1.Unprotect ...
Explicit reference to a worksheet by its sheet name: Worksheets("SOLL")...
Implicit reference to whatever worksheet is active at run-time: Range("N23:S25")..
There are many circumstances which falsify your assertion that "all commands of that macro refer to the ActiveSheet. For example, Sheet1 may (or may not) be the same as Worksheets("SOLL").
So, potentially this code is referring to as many as three different worksheet objects! Only the implicit, unqualified Range statements can be guaranteed to refer to the ActiveSheet.
Perhaps this test will shed some light on the matter. Modify your code as follows:
Private Sub Con1_Click()
MsgBox "Sheet1.Name is: " & Sheet1.Name
MsgBox "Worksheets("SOLL").CodeName is: " & Worksheets("SOLL").Codename
MsgBox "The activesheet is: " & ActiveSheet.Name
End Sub
You may also add the following information, which could be useful to those offering assistance:
Where is this button located? (On which worksheet?)
Where is the code for the button event located? In a sheet module or a standard module?
Related
I have been developing a VBA, UserForm1 program for the past few weeks and I have performed numerous test and never once had this problem.
The code, shown below, is part of the Initialization routine. I made some enhancements to the program, far deeper in the code than the code under discussion. Now, I can't get past this point in the program. I replaced "Activate" with "Select", but this did not affect the outcome, i.e., an aborted run.
Please, can someone suggest what I am doing wrong? Or, how can a program that been tested dozens of times, suddenly develop a fault out of thin air?
'======================================================================
Dim WPA() As Variant 'Workbook path for Category Class
Dim WBA() As Variant 'Workbook name for Category Class
Dim WSA() As Variant 'Worksheet name for Category Class
'======================================================================
Dim WBK() As Workbook
..........
Set WBK(S) = Workbooks.Open(WPA(S))
Workbooks(WBA(S)).Worksheets(WSA(S)).Activate 'Activate Worksheet
NWS(S) = CheckSheetExists(WSA(S)) 'Check for Worksheet
..........
Function CheckSheetExists(SheetName) As Boolean
CheckSheetExists = Evaluate("ISREF('" & SheetName & "'!A1)")
If CheckSheetExists = False Then
MsgBox "Worksheet " & SheetName & " does not exist. Run aborted."
End '<==================Run aborts
End If
End Function
It looks like you are using global variables?
If so, you should be aware that they are not as permanent as you might expect.
There are a number of things which cause global variables to reset and become undefined.
See this answer for more info: https://stackoverflow.com/a/7043901/1473412
Using "End"
An unhandled runtime error
Editing code
Closing the workbook containing the VB project
Is it possible that the "enhancements to the program, far deeper in the code than the code under discussion", causes one of these things to happen, and so resets the global variables?
If you have to use global variables, you could store them safely in a worksheet. Or, everytime you try and use one, you could check that it is defined, and if not, redefine it?
That is a good question, Storaxs' answer should have fixed this though to my knowledge.
However when something like this happens I try to create a whole new workbook and paste all of this code into it (just for the sheet in question) and then copy all the cells data into the new sheets.
This has saved me once before and it may help you in this case as well.
I also recommend not deleting your old workbook in case you don't port everything over.
If this still doesn't work let us know and we will continue to look into this.
I am running a function with a defined range and when trying to use End.(xlDown).Select I get a message "Select method of Range class failed".
I have edited the code below to show only the problem piece. After literally two hours of trying everything, I cannot get it to work.
The annoying part is that I have to use defined ranges since the function is part of a much larger Sub that doesn't work as intended once Select and Activate are used.
Function OutputFunction()
Dim rng8 As Range
Set rng8 = ThisWorkbook.Worksheets(5).Range("A2")
rng1.ClearContents 'Works like a charm.
rng2.Copy 'No problem here either.
rng8.End(xlDown).Select 'Fails misserably.
ActiveCell.Offset(0, 13).Select
Range(Selection, Range("N3")).Select
ActiveSheet.Paste
rng2.Copy destination:= rng8.parent.range(rng8.End(xlDown).Offset(0, 13), rng8.parent.Range("N3"))
"After literally two hours of trying everything, I cannot get it to work."
The first rule of Excel Macros: Never, ever, use SELECT in an Excel Macro.
The second rule of Excel Macros: Don't use Select in Excel Macros
The third.....
Try:
Option Explicit
Sub test()
Dim rng8 As Range
'Have in mind that you refer to a sheet based on it s index, NOT with its name!
'If sheets order change you will refer to another sheet
With ThisWorkbook.Worksheets(5)
Set rng8 = .Range("A2")
rng8.Select
.Range(rng8, rng8.End(xlDown)).Select
End With
End Sub
Try to use End(xlDown).Select in my personal macro. First I tested this in the original excel file that I wrote the macro in, and it worked in every step just fine. But the problem occurred when I used it in another file.
After some tests, I changed the .Select with .Activate and it worked. I'm not 100% sure whether we are talking on the same page or not, so tell me if this solved your problem, so I can improve my answer.
I want that whenever the workbook is closed a Sub from a sheet(sheet13) should be called, but as shown in the screenshot Im getting the error Invalid or unqualified reference.
By now I have tried
B2_Click
!Sheets("sheet13").B2_click
!Sheets("Employee Names").B2_click
Thisworkbook.Sheets("Employee Names").B2_click
And I am getting an error every time.
Your last example should work, so I would direct my troubleshooting towards the method you are calling. Try this in a new workbook:
In ThisWorkbook module
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Worksheets("Sheet13").Test
End Sub
And in Sheet13 module
Sub Test()
MsgBox "Closing"
End Sub
If that works, you have a starting point.
First off, the easy solution as already mentioned by others:
ThisWorkbook.Sheets("January").Something (string is case sensitive!)
There are multiple ways to reference a Sheet, and there are different types of names for worksheets. You have to differentiate between the "CodeName" and the "Name".
You will easily find more information on the difference between those two.
The main differences are, that the Name can be changed by the user, it is the name visible on the tab at the bottom of the Excel Interface.
The CodeName can only be changed through the VBA Interface.
The CodeName and the Name are usually identical when creating a worksheet (Sheet1, etc..).
To Reference a Sheet by Name you can use this code:
ThisWorkbook.Sheets("SomeName").SomeFunction()
The CodeName can be used directly like this: Sheet2.SomeFunction(), but I'd recommend changing the CodeName to something meaningful/expressive first!
(Name) is the CodeName, Name is the visible Name
More information on the Bang operator ! can be found here:
https://rubberduckvba.wordpress.com/2018/03/15/vba-trap-default-members/
Extensive Answer on referencing Sheets:
https://stackoverflow.com/a/41481428/10223558
I have an Excel file with one sheet called Master.
I have a button to delete a value from the database. This is what I have written that's relevant:
Sub delete_this()
On Error GoTo ErrorCatch
simple = Sheets("Master")
.........
ErrorCatch
MsgBox(Err.Description)
End Sub
It fails immediately when I use Sheets, saying "Application-defined or object-defined error." However I'm using other code that I know works as a reference and they called this no problem (though their file had multiple sheets).
Also in general I'm new to VBA and find it pretty unintuitive with its error messages, and finding out variable values. So any advice there would also be appreciated.
As mentioned in a comment by Scott Craner, you need to use the Set statement whenever assigning an object to a variable when using VBA. This little gotcha doesn't exist in VB.NET, and is easy to forget about these days.
Set simple = Sheets("Master")
My code is:
Sub zaa()
'code to find another open file that has the defined name "data"
' - useful where the name changes each month or week
For Each wb In Workbooks
On Error GoTo abcd
x = wb.Name
Workbooks(x).Activate
If Range("Data").Address <> "" Then y = wb.Name
Exit Sub
abcd:
Next wb
End Sub
Basic goal to find an Excel file with a specific named range when I know it exists but don't know the file name as it changes each week or month. Aim is to find the file and exit the sub at that point (or then do other code on that file and exit rather than going to other files.)
I find it works okay if I only have two files open but not if have more (unless the target one is second in line). Whilst I can run with what I have I thought others may benefit from what I have and I can have a more robust solution.
UPDATE:
Thanks to all those who have responded & to Mitch for putting original post in readable format! (I have since learnt how to correct that issue and to be able to copy code directly - which I have indicated in some comments below that I was having trouble with.)
I have had varying degrees of success - code from PaulStock & Reafidy both worked for me originally but PaulStock code has stopped working for me. Responses & code from Jean-François Corbett and Chris Neilsen have been helpful to me but presently when I run the code (as is) it appears to do nothing - ie doesn't leave the sheet I run it from (and that's not where the range name data appears). I was originally running the code in Excel 2007 in a sheet with other macros but to try and get different results have moved them to a stand alone sheet with no other macro files open. Have also tried running them from a file saved as '97-'03 format. Neither yielded different results. Others with more experience than I (see the errors I made evidenced in comments discussion with Reafidy & bear in mind that my original posted code was result of material found through google and modified by me for specific task & application and implication that I am not savvy enough to have come up with it on my own) may find other solutions better for them but for now:
I am more than happy as Reafidy's code works for me.
As I am not registered (I did try but it didn't work) and don't have enough reputation points I can not vote but I have put a check mark next to Reafidy's solution.
FURTHER UPDATE:
I have now discovered that PaulStock & Jean-François Corbett's code wasn't working for me due to a very minor discrepancy on my part - the code contained "Data" whilst my named range was "data". Making this change (to allow for case sensitivity) in their code means that both of their solutions now work for me and hence I have attempted to add a tick to their solutions! Unfortunately I have found that only one solution can have a tick.
I have also learnt that I took Chris' code too literally. In an effort to test each code 'as is' that is what I did. A simple additon of 'wb.activate' in the section where he has 'do stuff' makes the code do what I want.
THANKS again for all four contributions.
You're doing a few convoluted things in your code, and I think that's what's confusing the issue.
Having you error handler (abcd:) start in the middle of a For...Next loop is incredibly bad practice and can only lead to confusion. I can't think of a reason why this should ever be done.
Anyhow, there's no need to use error handling for this task. I understand that there are exceptional cases where bad VBA design forces us to use error handling instead of what should be built-in VBA functionality (e.g. testing whether a non-Variant array has been allocated), but this is not one of those cases. There's a standard way to do it (.Names collection of workbook object), and using error handling instead of this is messy and convoluted and asking for trouble.
Also, why say
x = wb.Name
Workbooks(x).Activate
when you can just say wb.Activate? You're not using x for anything. Or y, for that matter.
The following works and is both simplified and optimised, relative to your original code as well as to the other answers that have been given up to now:
Sub zaa2()
Dim wb As Workbook
Dim nm As Name
For Each wb In Workbooks
For Each nm In wb.Names
If nm.Name = "Data" Then
wb.Activate
Exit Sub
End If
Next
Next wb
End Sub
' A workbook containing a range named "Data" is now activated
' (if one is found amongst the open workbooks).
' Note that there may be more than one, but only the first found is activated...
EDIT: In your comment, you mention you had trouble due to confusion between uppercase "Data" and lowercase "data". To guard against this in the future, one possibility is to ignore case. This could be done as follows:
If StrComp(nm.Name, "data", vbTextCompare) = 0 Then
or
If LCase(nm.Name) = "data" Then
Both will return True if nm.Name is "Data", "data", "dATa", etc.
You can't use error handling like that. Either move the error handling out of the loop or reset the error handler each time it occurs, so use error handling like this:
A much preferred alternative would be:
Sub Test()
For Each wb In Workbooks
x = wb.Name
Workbooks(x).Activate
If RangeExists("Data") Then
y = wb.Name
Exit Sub
End If
Next wb
End Sub
Function RangeExists(s As String) As Boolean
On Error Resume Next
RangeExists = Range(s).Count > 0
End Function
EDIT:
#Jean-François Corbett, I have to say you a very quick to jump on the down vote button. The first solution I posted was because I made the assumption that the OP was not posting his entire code hence why I did not attempt to simplify it or "clean it up" like I usually do. I agree I did not word my answer well, but with regard to the first solution I was trying to demonstrate that he needed to reset the error handler. Unfortunately I should have said a "prefered alternative would be".
#Derek, Sorry I was unable to answer your further questions in time. Obviously you are free to choose whatever method you like. In my opinion the multiple loop solution provided by others which digs into the workbook name collection is unnecessary and long winded. Now more importantly the name collection can contain names which refer to a constant, formula, or a range. I presume you want to only check if the defined name is specifically a named range which means the looping method provided by others would need to be adjusted to be reliable.
I agree with comments made by others that error handling should be avoided BUT unnecessary looping in excel can be as much of an evil as using error handling and personally I avoid it like the plague.
The function above can be placed in its own module and be reused as much as you like. It is quick, reliable, avoids unnecessary looping, checks SPECIFICALLY for a named range in a workbook and is the most widely accepted/used method for checking if a named range exists within the excel vba community (by this I mean using a function and error handling over looping through the name collection). Do a google search for "Check If Named Range Exists" if you don't believe me. Or ask at www.ozgrid.com/forum if you want other excel vba experts opinion's.
Now that I know you have posted your entire code and that you did not intend to activate every workbook, you could use this code which will activate the first workbook found with the named range "data":
Sub Test3()
Dim wbLoop As Workbook
For Each wbLoop In Workbooks
If RangeExists("data", wbLoop) Then
wbLoop.Activate
Exit Sub
End If
Next wbLoop
End Sub
Function RangeExists(s As String, wb As Workbook) As Boolean
On Error Resume Next
RangeExists = wb.Names(s).RefersToRange.Count > 0
End Function
I completely understand the need for positive Criticism and I believe in the down vote system if it is used correctly. However, with two down votes for what I believe was a reasonable solution and along with my help with the ops formatting issues - unfortunately I cant help but feel like I want to distance myself from this forum.
Heres an alternative method without getting fancy with the error handler
Sub zaa()
Dim wb As Workbook
Dim CheckForNamedRange As Boolean
Dim nm As Name
On Error GoTo EH
For Each wb In Workbooks
CheckForNamedRange = True
Set nm = wb.Names("data")
If CheckForNamedRange Then
' Name found
' do stuff
Exit For
End If
Next
Exit Sub
EH:
If CheckForNamedRange Then
' Name not found
Err.Clear
CheckForNamedRange = False
Resume Next
Else
' Some other error occured, so handle it
'...
End If
End Sub
Try this code. Shouldn't have to worry about getting errors.
Sub zaa()
For Each wb In Workbooks
x = wb.Name
Workbooks(x).Activate
For Each n In Workbooks(x).Names
If n.Name = "Data" Then
y = wb.Name
Exit Sub
End If
Next
Next wb
End Sub