Comparing two Sheet objects (not contents) - vba

In the context of an error handling code, I would like to verify if the user has given to the current sheet the same name of another one into the same workbook (action forbidden, of course). So the way I intuitively tried to verify this was simply to loop through all the sheets and comparing the names:
For Each sh In ThisWorkbook.Sheets
If sh.Name = ThisWorkbook.ActiveSheet.Name Then
'error handling here
End If
Next sh
However, this is a huge logic fall in the case when:
1) The user is editing, let's say, the sheet number 3;
2) The sheet with the same name is at the position number 5;
In that case, the condition sh.Name = ThisWorkbook.ActiveSheet.Name would be met for sure because the sheet is compared to itself.
So, I wonder: how to understand if sh is not ThisWorkbook.ActiveSheet?
I had thought the task it could have simply been solved with a simple object comparison:
If (sh Is Not ThisWorkbook.ActiveSheet) And (sh.Name = ThisWorkbook.ActiveSheet.Name) Then
but this raises a compile error, namely Object does not support this property or method. Could anyone please help me finding the lack of logic in my code's structure?
OTHER INFORMATION
I have tried to manage the case through the Err.Description and the Err.Number, but the first is OS-language dependent and the second is the same for other types of error I need to handle differently.
Moreover, the sheets (names and contents) are contained into a .xlam add-in so the user can change the contents through custom user-forms but not through the Excel Application.
More in general, let's say that I would like to know how can I perform the comparison, even if a work-around in this specific case is possible, in order to use this method for future developments I already plan to do and that cannot be managed through the default VBA error handler.

Just check the index of the worksheet along with the name.
Only error (or whatever) if the name matches, but the index doesn't.
Option Explicit
Public Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.ActiveSheet
Dim wsToCheck As Worksheet
For Each wsToCheck In wb.Worksheets
If ws.Name = wsToCheck.Name And ws.Index <> wsToCheck.Index Then
'do something
End If
Next
End Sub
Of course, you could always just test for object equality using the Is operator too, or inequality in your specific case.
Public Sub test2()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.ActiveSheet
Dim wsToCheck As Worksheet
For Each wsToCheck In wb.Worksheets
If Not ws Is wsToCheck Then
'do something
Debug.Print ws.Name
End If
Next
End Sub

You've got an incorrect syntax with "Not"; it should be this:
If (Not sh Is ThisWorkbook.ActiveSheet) And (sh.Name = ThisWorkbook.ActiveSheet.Name) Then

There's no reason to loop through the collection of sheets. Use this:
Function IsWshExists(ByVal wbk As Workbook, ByVal wshName As String) As Boolean
Dim wsh As Worksheet
On Error Resume Next
Set wsh = wbk.Worksheets(wshName)
IsWshExists = (Err.Number = 0)
Set wsh = Nothing
End Function
Usage:
If Not IsWshExists(ThisWorkbook, "Sheet2") Then
'you can add worksheet ;)
'your logic here
End If

Related

VBA Code for Vlookup on different worksheets within the same workbook

I am trying to write a vba script that will allow me to vlookup values from Sheet(3) to different Sheet(i) - and paste it on range"R2" on the Sheet(i) - I also want it to go to the end of the values in Column M on Sheet(i) [if this is possible]. I basically want to run through all the different "i" sheets on the workbook. Sheet (3) has all the data that needs to be copied on all the other "i" sheets.
I keep getting an error with my code below.
Sub CopyTableau1Data()
Dim wka As Worksheet
Dim wkb As Worksheet
ShtCount = ActiveWorkbook.Sheets.Count
For i = 9 To ShtCount
With ThisWorkbook
Set wka = .Sheets(i)
Set wkb = .Sheets(3)
End With
Worksheets(i).Activate
If IsError(Application.WorksheetFunction.VLookup(wka.Range("M2"), wkb.Range("E:T"), 14, 0)) Then
wka.Range("R2").Value = ""
Else
wka.Range("R2").Value = Application.WorksheetFunction.VLookup(wka.Range("M2"), wks.Range("E:T"), 14, 0)
End If
Next i
End Sub
IsError does not work with Application.WorksheetFunction.VLookup or WorksheetFunction.VLookup, only with Application.VLookup.
It is faster and easier to return Application.Match once to a variant type variable and then test that for use.
dim am as variant
'are you sure you want wkb and not wks here?
am = Application.match(wka.Range("M2"), wkb.Range("E:E"), 0)
If IsError(am) Then
wka.Range("R2") = vbnullstring
Else
'are you sure you want wks and not wkb here?
wka.Range("R2") = wks.cells(am, "R").value
End If
Note the apparent misuse of wkb and wks in two places. I don't see the point of looking up a value in one worksheet, testing that return then using the results of the test to lookup the same value in another worksheet.
You can use the following code:
Sub CopyTableau1Data()
Dim wka As Worksheet
Dim wkb As Worksheet, i As Integer
ShtCount = ActiveWorkbook.Sheets.Count
For i = 9 To ShtCount
With ThisWorkbook
Set wka = .Sheets(i)
Set wkb = .Sheets(3)
End With
Worksheets(i).Activate
wka.Range("R2") = aVL(i)
Next i
End Sub
Function aVL(ByVal wsno As Integer) As String
On Error GoTo errhandler:
aVL =
Application.WorksheetFunction.VLookup(ActiveWorkbook.Worksheets(wsno).Range("M2"),
ActiveWorkbook.Worksheets(3).Range("E:T"), 14, False)
errhandler:
aVL = ""
End Function
When you try to check an error by isError, program flow can immediately return from the sub depending on the error. You could use on error in your sub to prevent that but this would only handle the first error. By delegating the error handling to a function you can repeatedly handle errors in your loop.
I assumed you wanted to use wkb.Range("E:T") instead of wks.Range("E:T").

I am unable to get the correct worksheet name when looping through a workbook

I am trying to collect data from multiple workbooks by checking the worksheet names.
However when i run my code to check the worksheetname(which is Raw Data), i am getting a false result. The code is returning only Sheet1 and Sheet2.
Below is the code:
Function WorksheetRAWExists(wsName As String) As Boolean
Dim ws As Worksheet
Dim ret As Boolean
ret = False
wsName = UCase(wsName)
For Each ws In ThisWorkbook.Sheets
If UCase(ws.Name) = "RAW DATA" Then
ret = True
Exit For
End If
Next
WorksheetRAWExists = ret
End Function
This happens because you loop through "ThisWorkbook" in your for-each, which always checks the worksheet collection in the workbook you're running the VBA code from.
If you're looking to loop through all sheets in all open workbooks then you could do something like so:
Sub test()
Dim wbk As Workbook, ws As Worksheet
For Each wbk In Workbooks
For Each ws In wbk.Worksheets
MsgBox ws.Name
Next ws
Next wbk
End Sub
Edit:
You could also pass the workbook name or index (or the workbook reference itself) to your function and check that specific reference in the workbook collection, in case looping through all open workbooks isn't what you want.

Copying range of cells from workbooks based on sheetname

I had received help yesterday on this code, but I'm completely new to VBA so I'm having difficulties. To explain my code:
I am trying to copy a range of cells from one workbook to the same range of cells in another workbook, but the names of the worksheets have to be the same. So the code is supposed to test if the worksheets exist, then it'll find the corresponding worksheets in the two workbooks. If the names are the same, it'll take on the value, but if not, it'll keep going through all the sheets in workbook1 to find the right sheet. The code runs through, but it's not copying the cells.
I assume the issue could stem from the sheetexists line within the first loop. I was told I need to make sure that I check to see if the sheets exist before running the loops, but I'm unsure of how to do that.
Thank you!
Function SheetExists(shtName As String, Optional wb As Workbook) As Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
SheetExists = Not sht Is Nothing
End Function
Sub foo()
Dim wbk1 As Workbook
Dim wbk2 As Workbook
Dim shtName1 As String
Dim shtName2 As String
Dim i As Integer
Dim p As Integer
Set wkb1 = Workbooks.Open("C:\Users\lliao\Documents\Trad Reconciliation.xlsx")
Set wkb2 = Workbooks.Open("C:\Users\lliao\Documents\TradReconciliation.xlsx")
i = 2
p = 2
shtName2 = wkb2.Sheets(i).Name
shtName1 = wkb1.Sheets(p).Name
For i = 2 To wkb2.Worksheets.Count
If (SheetExists(shtName2) = True) And (SheetExists(shtName1) = True) Then
For p = 2 To wkb1.Worksheets.Count
If shtName2 = shtName1 Then
wkb2.Sheets(shtName2).Range("D2:G2").Value = wkb1.Sheets(shtName1).Range("D2:G2").Value
End If
Next p
End If
Next i
End Sub
You set shtName2 = wkb2.Sheets(i).Name but then never change it again. So it is always looking at one sheet only.
Your foo subroutine should be changed to:
Sub foo()
Dim wbk1 As Workbook
Dim wbk2 As Workbook
Dim i As Integer
Set wbk1 = Workbooks.Open("C:\Users\lliao\Documents\Trad Reconciliation.xlsx")
Set wbk2 = Workbooks.Open("C:\Users\lliao\Documents\TradReconciliation.xlsx")
For i = 2 To wbk2.Worksheets.Count
If SheetExists(wbk2.Worksheets(i).Name, wbk1) Then
wbk2.Worksheets(i).Range("D2:G2").Value = wbk1.Worksheets(wbk2.Worksheets(i).Name).Range("D2:G2").Value
End If
Next i
End Sub
It is also a good idea to include Option Explicit as the first line of your code module. I had typos in my answer because I had copy/pasted your original code, but you had defined variables wbk1 and wbk2 and then used wkb1 and wkb2 instead. As wkb1 and wkb2 weren't explicitly declared, they were implicitly created as Variant, which then led to problems in code which expected Workbook.
Option Explicit instructs the compiler to force you to explicitly declare all variables, thus picking up such typos.

Can't use objects from a worksheet when the worksheet is declared

So The following works
Sheet1.btnAfkeurMin.Visible = True
Below is what doesn't work
Dim WS As Worksheet
For Each WS In ThisWorkbook.Worksheets
If cmbDate.Value = WS.Name Then
WS.btnAfkeurMin.Visible = True
end if
next
cmdDate.Value and WS.Name are the same. (Checked in console)
How do I call btnAfkeurMin while using WS?
I suspect the issue is caused because btnAfkeurmin is only assigned once. As you loop through the worksheets, you need to re-reference the new button on the new sheet.
For example, this code will loop through each sheet and hide all of the buttons named btnAfkeurmin.
Option Explicit
Sub TestFormControls()
Dim WS As Worksheet
For Each WS In ThisWorkbook.Worksheets
WS.Shapes("btnAfkeurMin").Visible = msoFalse
Next WS
End Sub
Adapting to your code:
Option Explicit
Sub TestFormControls()
Dim WS As Worksheet
For Each WS In ThisWorkbook.Worksheets
If cmbDate.Value = WS.Name Then
WS.Shapes("btnAfkeurMin").Visible = True
End If
Next
End Sub
Similar logic applies if you need to reference different comboboxes named cmbDate. If you are always referencing the single one that is declared earlier in the code, this is fine (which I gather is what's happening based on your statement that cmbDate.Value = WS.Name evaluates to true).
But if, for some reason, you had a different combobox on each sheet that you needed to reference you would do so in the same way with:
WS.Shapes("cmbDate").Value
Use
WS.OLEObjects("btnAfkeurMin").Visible = True
If you want to use the Object directly then you can also use this
Worksheets(WS.Name).btnAfkeurMin.Visible = True

Excel: Lookup table name excel for a given variable

I have one Workbook with multiple projects. Each project has it's own sheet. In each of these sheets there are columns for order numbers ("OrderNub").
In another sheet called "MasterList" contains all of the order numbers across all project. This list is in column A.
I need a function or Macro that will search all of my sheets (bar MasterList) and will display the sheet name in column B.
Below is what I have in Excel:
Option Explicit
Function FindMyOrderNumber(strOrder As String) As String
Dim ws As Worksheet
Dim rng As Range
For Each ws In Worksheets
If ws.CodeName <> "MasterList" Then
Set rng = Nothing
On Error Resume Next
FindMyOrderNumber = ws.Name
On Error GoTo 0
If Not rng Is Nothing Then
FindMyOrderNumber = ws.Range("A1").Value
Exit For
End If
End If
Next
Set rng = Nothing
Set ws = Nothing
End Function
Option Explicit
Function FindMyOrderNumber(strOrder As String) As String
Dim ws As Worksheet
Dim rng As Range
For Each ws In Worksheets
If ws.CodeName <> "MasterList" Then
Set rng = Nothing
On Error Resume Next
Set rng = ws.Columns(1).Find(strOrder)
On Error GoTo 0
If Not rng Is Nothing Then
FindMyOrderNumber = ws.Name
Exit For
End If
End If
Next
Set rng = Nothing
Set ws = Nothing
End Function
Assumptions:
Your project sheets us Table objects. If they don't, you need to edit line 11 to point to whatever range contains the OrderNub data.
If not tables, then your projects at least use the exact same layout. In that case, you could change line 11 to something like: Set rng = ws.Range("C1").EntireColumn.Find(strOrder)
The code name of the master list is MasterList. This is not the same as the worksheet name as seen on the tab. This is the VBA code name. I prefer to use that as it is less likely to be changed and break the check. You can find the codename in the VBA editor. For instance, in this screenshot, the codename for the first worksheet is shtAgingTable and the name - as shown on the tab in Excel - is AgingTable.
This is a function, not a subroutine. That means you don't run it once. It's meant to be used like any other built-in function like SUM, IF, whatever. For instance, you can use the following formula in cell B2:
=FindMyOrderNumber($A2)