Using worksheet name as variable - excel

Please can someone help me out, I seem to be going around in circles with my problem?
I have a workbook with 4 worksheets Comparison, Office1, Office2 and Office3. On the Comparison sheet the other sheet names are listed in column A. In column B on this sheet I have a button.
What I want to do is double click the button (I have sorted the code for this) and this will then take you to cell D5 on the corresponding sheet.
At the moment I have the following code but it doesn't seem to activate the Office sheet it uses the comparison sheet.
Could anyone please let me know what I am missing?
Thanks
Sub OfficeSht()
Dim rCrit3 As Range
Dim wb As Workbook
Dim ws As Worksheet
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set rCrit3 = ActiveCell.Offset(RowOffset:=0, ColumnOffset:=-2)
Debug.Print rCrit3
Set ws = rCrit3.Worksheet
ws.Activate
ActiveSheet.Range("D5").Select
Application.EnableEvents = True
End Sub

Application.Goto seems appropriate and reduces the steps to achieve your goal.
Sub OfficeSht()
Dim ws As string
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
ws = ActiveCell.Offset(RowOffset:=0, ColumnOffset:=-2).value2
Debug.Print "'" & ws & "'!D5"
Application.Goto Reference:="'" & ws & "'!R5C4" '<~~ D5 in xlR1C1
With Application
.EnableEvents = true
.ScreenUpdating = true
End With
End Sub
You may want make this a Worksheet_BeforeDoubleClick event.

You don't say what kind of button you have so I've give a couple of examples.
One piece of code that is common in all examples is WorkSheetExists which checks if the sheet name corresponds to a worksheet.
Public Function WorkSheetExists(SheetName As String) As Boolean
Dim wrkSht As Worksheet
On Error Resume Next
Set wrkSht = Worksheets(SheetName)
WorkSheetExists = (Err.Number = 0)
Set wrkSht = Nothing
On Error GoTo 0
End Function
Next are three ways to call the OfficeSht procedure.
If your buttons are ActiveX button and are in column B you can use:
Private Sub CommandButton1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
OfficeSht Me.Shapes("CommandButton1").TopLeftCell
End Sub
This code returns a reference to Top Left Cell that your button sits in - assuming your sheet name is one cell to the left of this.
The second way is if you're using a Form button.
Public Sub Button_Click()
OfficeSht Me.Shapes(Application.Caller).TopLeftCell
End Sub
Again, it returns a reference to the Top Left Cell that the button is placed in. When you add the button just assign it to the Button_Click procedure.
The third way assumes your button is actually a cell formatted to look like a button, or if you just want to double-click the sheet name in column A and do away with having a button in column B:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
OfficeSht Target
End Sub
Finally, your code to select cell D5 (Row 5, Column 4 - R5C4).
If you're clicking, or referenced cell is in, column B:
Public Sub OfficeSht(ByVal Target As Range)
Dim rLastCell As Range
Dim rDataRange As Range
Set rLastCell = Cells(Rows.Count, 1).End(xlUp)
Set rDataRange = Range("A1", rLastCell)
If Not Intersect(Target, rDataRange.Offset(, 1)) Is Nothing Then
If WorkSheetExists(Target.Offset(, -1).Value) Then
Application.Goto "'" & Target.Offset(, -1).Value & "'!R5C4"
End If
End If
End Sub
If you're clicking, or referenced cell is in, column A:
Public Sub OfficeSht(ByVal Target As Range)
Dim rLastCell As Range
Dim rDataRange As Range
Set rLastCell = Cells(Rows.Count, 1).End(xlUp)
Set rDataRange = Range("A1", rLastCell)
If Not Intersect(Target, rDataRange) Is Nothing Then
If WorkSheetExists(Target.Value) Then
Application.Goto "'" & Target.Value & "'!R5C4"
End If
End If
End Sub
Might've waffled on a bit there.... :)

Related

Two Excel macros seem to conflict and prevent each other from working

I've been trying to write some macros to a cross-departmental spreadsheet, which when I press a command button will essentially "archive" a row of work. I also have one which is meant to auto-capitalise a column when people type in it. See below:
This is the Archive macro:
Sub Archive()
If MsgBox("Do you want to archive the selected row?" & vbNewLine & vbNewLine & "Row should only be archived after x has passed.", vbYesNo, "Archive") = vbNo Then Exit Sub
For Each WSheet In ActiveWorkbook.Worksheets
If WSheet.AutoFilterMode Then
If WSheet.FilterMode Then
WSheet.ShowAllData
End If
End If
For Each DTable In WSheet.ListObjects
If DTable.ShowAutoFilter Then
DTable.Range.AutoFilter
DTable.Range.AutoFilter
End If
Next DTable
Next WSheet
'Declare variables
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim lastRow As Long
'Set variables
Set sht1 = Sheets("xDepartment")
Set sht2 = Sheets("Archive")
'Select Entire Row
Selection.EntireRow.Select
'Move row to destination sheet & Delete source row
lastRow = sht2.Range("A" & sht2.Rows.Count).End(xlUp).Row
With Selection
.Copy Destination:=sht2.Range("A" & lastRow + 1)
.EntireRow.Delete
End With
End Sub
The autocapitalisation macro is attached to the specific sheet? (i.e., it's attached when right-clicking on "xDepartment" and selecting "View code" - not sure if that has something to do with it?). There's also a macro on this sheet which calculates the date that data in a certain cell is changed:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A1 As Range
Set A1 = Range("O:O,Q:Q,T:T,W:W")
If Not Intersect(Target, A1) Is Nothing Then
Application.EnableEvents = False
Target.Value = UCase(Target.Value)
Application.EnableEvents = True
End If
Dim WorkRng As Range
Dim Rng As Range
Dim xOffsetColumn As Integer
Set WorkRng = Intersect(Application.ActiveSheet.Range("O:O, Q:Q"), Target)
xOffsetColumn = 1
If Not WorkRng Is Nothing Then
Application.EnableEvents = False
For Each Rng In WorkRng
If Not VBA.IsEmpty(Rng.Value) Then
Rng.Offset(0, xOffsetColumn).Value = Now
Rng.Offset(0, xOffsetColumn).NumberFormat = "dd/mm/yyyy"
Else
Rng.Offset(0, xOffsetColumn).ClearContents
End If
Next
Application.EnableEvents = True
End If
End Sub
The error that comes up is "Run-time error '13': Type mismatch". Do you know why this might be happening?
Thanks in advance!
After your First line in the Archive macro put
Application.EnableEvents = False
On error goto Whoops
Then just above your End Sub for that macro put
Whoops:
Application.EnableEvents = True
This will turn off the other macro while your archive is running
Your code to move the row from xDepartment worksheet to Archive worksheet includes the line,
Selection.EntireRow.Select
This makes the xDepartment worksheet active. The code to actually move the row and remove the original does nothing to change the xDepartment as the ActiveSheet.
With Selection
.Copy Destination:=sht2.Range("A" & lastRow + 1)
.EntireRow.Delete
End With
In your worksheet_change, you have,
Set WorkRng = Intersect(Application.ActiveSheet.Range("O:O, Q:Q"), Target)
So your Archive worksheet's Worksheet_Change is going to try to work on the xDepartment worksheet.
But you delete the row so it no longer exists; hen ce:
Run-time error '13': Type mismatch
Set your WorkRng with,
Set WorkRng = Intersect(Range("O:O, Q:Q"), Target)
It is in a private sub procedure on the Archive's private code sheet so there is no need to specify a parent worksheet unless you specifically want to work on another worksheet.
Avoid the use of ActiveSheet, Select, Selection and Activate whenever possible and never use them in a worksheet's private code sheet to refer to that worksheet.

Run-Time Error 1004 when attempting to lock cell in worksheet protected with UserInterface Only := True

Build a new excel workbook and add the following code to an inserted module1.
In the worksheet enter:
"Test FixDate" to cell A1.
Merge and Center cells F1:H1.
Unlock Cells F1.
Enter formula in F1 = Today()
In the Formula Tab Define Name TodaysDate as $F$1:$H$1.
Rename the worksheet "Test"
In the Immediate window type Protect and press Enter.
Next type FixDate and press Enter.
A run-time error 1004 occurs on line:
rng.Locked = True
Note that the value in Range TodaysDate is changed to the text provided by the message box, but the cell properties of the range cannot be changed unless the worksheet is unprotected. I wish to change the color of the range TodaysDate to match the color of Cell A1, too. This property change also fails with run-time 1004. I have omitted the attempt from the code in building the example to be as simple as possible.
Is this an Excel bug? or have I missed some restriction about setting the range properties?
Here is the code:
Option Explicit
Global Const gPassword As String = "password"
Sub FixDate()
Dim rng As Range
Dim wks As Worksheet
Set wks = Worksheets("Test")
wks.Activate
Set rng = Range("TodaysDate")
If ActiveWorkbook.FileFormat <> xlOpenXMLTemplateMacroEnabled _
And ActiveWorkbook.FileFormat <> xlOpenXMLTemplate _
And ActiveWorkbook.FileFormat <> xlTemplate Then
If Not rng.Locked Then
' Let user change the date; today's date is default
rng.Value = InputBox("Enter competition date (mm/dd/yy), if not today.", Range("A1"), Format(Now(), "mm/dd/yy"))
rng.Copy
rng.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=False
Application.CutCopyMode = False
rng.Locked = True
End If
End If
Application.Goto Reference:=rng, Scroll:=False
Set rng = Nothing
Set wks = Nothing
End Sub
Public Sub ProtectWorkbook(Optional UnProtect As Boolean = False)
'
' Workbook is protected (or optionally unprotected) in such a way as to allow code to change the data
' without unprotecting worksheets but human interface needs password.
'
Dim wks As Worksheet
Dim wksActive As Worksheet
Dim i As Integer
Set wksActive = ActiveSheet
For Each wks In Worksheets
With wks
If .Name = "Roster" Then
On Error Resume Next
.Visible = xlSheetHidden
End If
wks.UnProtect Password:=gPassword
If UnProtect = False Then wks.Protect Password:=gPassword, UserInterfaceOnly:=True
End With
Next wks
Set wks = Nothing
wksActive.Activate
Set wksActive = Nothing
End Sub
Public Sub Protect()
ProtectWorkbook UnProtect:=False
End Sub
Public Sub UnProtect()
ProtectWorkbook UnProtect:=True
End Sub
Remove the offending code (rng.locked = true) and replace with
Selection.Locked = True
Selection.Interior.ColorIndex = wks.Range(Cells(1, 1), Cells(1, 1)).Interior.ColorIndex
This code runs OK without unprotecting the sheet.
It still does not explain why the Range object rng fails. I would be interested in any comment about that.
Thanks

(VBA) apply to all, except one specific

Hi everyone i made a button on excel using VBA modules,The code works on the active sheet but what im looking for is to be applied to more sheets, not just the active sheet where the button is placed.
Sub Botón1_Haga_clic_en()
Call Worksheet_Calculate
End Sub
'apply cells colors from single-cell formula dependencies/links
Private Sub Worksheet_Calculate()
Dim Cel As Range
Dim RefCel As Range
On Error Resume Next
For Each Cel In ActiveSheet.UsedRange
If Cel.HasFormula Then
Set RefCel = Evaluate(Mid(Cel.Formula, 2))
Cel.Interior.Color = RefCel.Interior.Color
End If
Next Cel
End Sub
Try the code below :
Option Explicit
Sub Botón1_Haga_clic_en()
Dim wsName As String
Dim ws As Worksheet
wsName = ActiveSheet.Name
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name Like wsName Then '<-- is worksheet's name doesn't equal the ActiveSheet's
ApplyCellColors ws ' <-- call you Sub, with the worksheet object
End If
Next ws
End Sub
'=======================================================================
'apply cells colors from single-cell formula dependencies/links
Private Sub ApplyCellColors(ws As Worksheet)
Dim Cel As Range
Dim RefCel As Range
On Error Resume Next
For Each Cel In ws.UsedRange
If Cel.HasFormula Then
Set RefCel = Evaluate(Mid(Cel.Formula, 2))
Cel.Interior.Color = RefCel.Interior.Color
End If
Next Cel
End Sub
Your problem can be translated to something like How to loop over all sheets and ignore one of them?
This is a good way to do it:
Option Explicit
Option Private Module
Public Sub TestMe()
Dim wks As Worksheet
For Each wks In ThisWorkbook.Worksheets
If wks.name = "main" Then
Debug.Print "Do nothing here, this is the active sheet's name"
Else
Debug.Print wks.name
End If
Next wks
End Sub
Pretty sure, that you should be able to fit it in your code.

Worksheet_change not working when cell content changes via VBA but does manually

I am trying to color the background of all cells in column B whose content has changed via VBA.
The background changes if I manually update the cells but not when it changes via VBA. I can not get why it is not changing with the VBA.
In the worksheet module for the sheet called OriginalData I have
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
Dim nName As String, nEmail As String
Application.EnableEvents = False
For Each c In Target
If c.Column = 2 And Target <> "" Then
c.Interior.Color = RGB(255, 255, 0)
End If
Next c
Application.EnableEvents = True
End Sub
I am updating the Column 2 on OriginalData with
Sub FindReplace_Updated_UnMatched_NAMES_Original_Prepperd_2()
Dim FindValues As Variant
Dim ReplaceValues As Variant
Dim wsFR As Excel.Worksheet
Dim wsTarget As Excel.Worksheet
Dim lRow As Long
Dim i As Long
Sheets("Updated_UnMatched").Select
Set wsFR = ThisWorkbook.Worksheets("Updated_UnMatched")
Set wsTarget = ThisWorkbook.Worksheets("OriginalData")
lRow = wsFR.Range("C" & wsFR.Rows.Count).End(xlUp).Row
FindValues = wsFR.Range("C1:C" & lRow).Value
ReplaceValues = wsFR.Range("D1:D" & lRow).Value
With wsTarget
If IsArray(FindValues) Then
For i = 2 To UBound(FindValues)
.Columns("B:B").Replace FindValues(i, 1), ReplaceValues(i, 1), xlWhole, xlByColumns, False
Next i
Else
End If
End With
End Sub
You likely errored out on Target <> "" and got stuck with Application.EnableEvents = False environment state.
First, go to the VBE's Immediate Windows (Ctrl+G) and enter the command Application.EnableEvents = True. While in the VBE, make this modification to your code for multiple Target cell counts.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
Dim nName As String, nEmail As String
Application.EnableEvents = False
For Each c In Target
If c.Column = 2 And c.Value <> "" Then '<~~ c <> "", not Target <> ""
c.Interior.Color = RGB(255, 255, 0)
End If
Next c
Application.EnableEvents = True
End Sub
That should be enough to get you going.
When there is some errors during event handler execution, it doesn't work properly for next times. You can find and fix the errors and it will work properly.
As a quick fix, you can do these steps:
Add On Error Resume Next at the beginning of Worksheet_Change to
prevent errors make your code stop working.
Save your workbook in a macro enabled format and reopen it enabling
active content.
Run macro and it will work properly.
I tested your code and it worked for me in Excel 2013.
It is strongly recommended to fix your errors instead of hiding them using On Error Resume Next.

I have a VBA code entered on Sheet 1. I want the code to work on Sheet 2. Do i just copy the code from Sheet 1 to Sheet 2?

I found this VBA code on here that works great. I want the code to work on the other worksheets in the workbook. The code works great in Sheet 1 but I would like the code to work on Sheet 2, Sheet 3, etc. as well. I tried copying the code from the Sheet 1 Module and pasted it into Sheet 2, Sheet 3, etc. to see if the code works. The code doesn't quite work as I anticipated it. I think I need to do something with the Standard Module code so that the code will work properly.
Sheet 1 Module
Private Sub Worksheet_Calculate()
Dim rng As Range, c As Range
Dim rngToColor As Range
On Error GoTo ErrorHandler
Application.EnableEvents = False
'get only used part of the sheet
Set rng = Intersect(Me.UsedRange, Me.Range("A:Z"))
If rng Is Nothing Then GoTo ExitHere
For Each c In rng
'check if previous value of this cell not equal to current value
If cVals(c.Address) <> c.Text Then
'if so (they're not equal), remember this cell
c.ClearComments
c.AddComment Text:="Changed value from '" & cVals(c.Address) & "' to '" & c.Text & "'" & " on " & Format(Date, "mm-dd-yyyy") & " by " & Environ("UserName")
c.Interior.ColorIndex = 36
End If
'store current value of cell in dictionary (with key=cell address)
cVals(c.Address) = c.Text
Next c
ExitHere:
Application.EnableEvents = True
Exit Sub
ErrorHandler:
Resume ExitHere
End Sub
ThisWorkbook Module
Private Sub Workbook_Open()
Application.Calculation = xlCalculationManual
Call populateDict
Application.Calculation = xlCalculationAutomatic
End Sub
Standard Module
Public cVals As New Dictionary
Sub populateDict()
Dim rng As Range, c As Range
With ThisWorkbook.Worksheets("Sheet1")
Set rng = Intersect(.UsedRange, .Range("A:Z"))
For Each c In rng
cVals(c.Address) = c.Text
Next c
.Calculate
End With
End Sub
Edit: I took the standard module and revised it to:
Sub populateDict()
Dim rng As Range, c As Range
With ThisWorkbook.Worksheets("Sheet1")
Set rng = Intersect(.UsedRange, .Range("A:Z"))
For Each c In rng
cVals(c.Address) = c.Text
Next c
.Calculate
End With
With ThisWorkbook.Worksheets("Sheet2")
Set rng = Intersect(.UsedRange, .Range("A:Z"))
For Each c In rng
cVals(c.Address) = c.Text
Next c
.Calculate
End With
End Sub
this edit almost does the trick, but not sure why code isn't working correctly
One way to do this is by placing the code in a separate module and then set your active sheet to a variable like this:
Sub myScript()
Dim wks As Worksheet
Set wks = ActiveSheet
MsgBox (wks.Range("A1"))
End Sub
If you call this with Sheet1 active it will return the value from Sheet1.
Another method is by passing in the sheet as a variable to the sub. Here is just one way to do this. Add a button to each sheet that you want the macro to run from. Double click each button in 'Design Mode' so that the VBA click event is opened in the editor. Add a call to your sub like this:
Private Sub CommandButton1_Click()
Call myScriptPass(ActiveSheet)
'Or you can qualify it like this
Call myScriptPass(Sheets(1))
End Sub
Now change your macro to this: (still located in a separate module)
Sub myScriptPass(wks As Worksheet)
MsgBox (wks.Range("A1"))
End Sub
EDIT
Using the code you added to your post you can change it to the following:
Public cVals As New Dictionary
Sub record()
Dim wks As Worksheet
Set wks = ActiveSheet
Dim rng As Range, c As Range
With wks
Set rng = Intersect(.UsedRange, .Range("A:Z"))
For Each c In rng
cVals(c.Address) = c.Text
Next c
.Calculate
End With
End Sub
Now, it will run for which ever sheet is active. So if you call the macro via button on Sheet1, then the code will run on Sheet1.
Loop from main program
Public cVals As New Dictionary
Sub myMainProgram()
Dim wks As Worksheet
'Loop thru each sheet in workbook example
For Each wks In Worksheets
Call record(wks)
Next wks
'Call subroutine for specific sheet example
Call record(sheets("sheet1"))
End Sub
Sub record(wks As Worksheet)
Dim rng As Range, c As Range
With wks
Set rng = Intersect(.UsedRange, .Range("A:Z"))
For Each c In rng
cVals(c.Address) = c.Text
Next c
.Calculate
End With
MsgBox ("Record macro was run on " & wks.Name & " worksheet.")
End Sub

Resources