I have a stylized spreadsheet with formatted cells for users to input data. I am trying to create a macro for use with a button to clear all of the input cells at once. However, I am struggling with the "find" and "findformat" functions.
To make it simple, in this code I am just looking for cells that say "Retail". When I run the code, the value of myRange is always Nothing even though there is clearly a cell in the spreadsheet that has the value "Retail". Any ideas why the range is Nothing?
Public Sub reset()
'reset all input fields to no value
msg = MsgBox("Are you sure you want to delete all data and reset all files to original state?", vbYesNoCancel, "***Warning***")
If msg = vbYes Then
Dim inputCell As Long
Dim noteCell As Long
inputCell = RGB(255, 204, 153)
noteCell = RGB(255, 255, 204)
Dim myRange As Range
Dim mySheet As Worksheet
Dim shp As Shape
Dim sht As Worksheet
Dim objXL As Object
Dim wb As Workbook
Dim pathName, name, myLink As String
Set sht = ActiveSheet
Set wb = ActiveWorkbook
pathName = wb.FullName
name = wb.name
For Each shp In sht.Shapes
If shp.Type = msoGroup Then
For i = 1 To shp.GroupItems.Count
If shp.GroupItems(i).Type = msoEmbeddedOLEObject Then
shp.GroupItems(i).Select
shp.GroupItems(i).OLEFormat.Activate
Set wb = ActiveWorkbook
If Not IsEmpty(wb.LinkSources(xlExcelLinks)) Then
For Each link In wb.LinkSources(xlExcelLinks)
On Error Resume Next
wb.ChangeLink name:=link, newName:=pathName, Type:=xlLinkTypeExcelLinks
Next link
End If
For Each mySheet In ActiveWorkbook.Worksheets
With Application.FindFormat.Interior.Color = inputCell
myRange = mySheet.Cells.Find(what:="Retail") ', searchformat:=True)
myRange.ClearContents
End With
Next mySheet
wb.Close (False)
End If
Next i
End If
Next shp
End If
End Sub
I refer to the FindFormat documentation for some example:
https://msdn.microsoft.com/en-us/library/office/ff838023.aspx
And modify your code thusly:
With Application.FindFormat
.Interior.Color = inputCell
Do
Set myRange = mySheet.Cells.Find(what:="Retail", SearchFormat:=True)
If myRange Is Nothing Then myRange.ClearContents
Loop While Not myRange Is Nothing
End With
NOTE: You should be using the Set keyword when assigning to a range object myRange. Also, your improper use of On Error Resume Next may be masking additional errors which are adversely impacting the results of this function. You can rectify the latter issue like so:
If Not IsEmpty(wb.LinkSources(xlExcelLinks)) Then
For Each link In wb.LinkSources(xlExcelLinks)
On Error Resume Next
wb.ChangeLink name:=link, newName:=pathName, Type:=xlLinkTypeExcelLinks
On Error GoTo 0 '### RESUME NORMAL ERROR HANDLING
Next link
End If
I changed my code as follows and now it works exactly as I wanted:
For Each mySheet In ActiveWorkbook.Worksheets
With Application.FindFormat
.Interior.Color = inputCell
Do
On Error GoTo handler:
Set myRange = mySheet.Cells.Find(what:="?*", searchformat:=True).MergeArea
If Not (myRange Is Nothing) Then
myRange.ClearContents
End If
Loop While Not (myRange Is Nothing)
.Interior.Color = noteCell
Do
On Error GoTo handler:
Set myRange = mySheet.Cells.Find(what:="?*", searchformat:=True).MergeArea
If Not (myRange Is Nothing) Then
myRange.ClearContents
End If
Loop While Not (myRange Is Nothing)
handler:
Set myRange = Nothing
Resume Next
End With
Next mySheet
I'm just not sure if this kind of error handling is the best way to deal with the problem, and I don't understand why an error occurs in the first place. So if anyone has a thought on this, I would appreciate it. If not, I'm just happy it works now.
Related
This is my first post on here and I have very little formal training in coding, so this is probably a very easy problem.
I'm running into an error 9, VBA Subscript out of range, when running macros defined by the code below.
Specifically, it is the Sub Select_Last() function. Excel does not like the subsequent expression, however if this is used on its own in a separate Excel file then it works fine.
I think the problem is that Excel's default variable (Activesheet etc) are conflicting with each other. But I am not sure how to remedy this. The other subs work fine. Can anyone help? Thank you.
Public lastsheet As String
Sub Select_Last()
Sheets(lastsheet).Select
End Sub
Sub Protect()
For i = 1 To Sheets.Count
Sheets(i).Protect
Next i
End Sub
Sub UnProtect()
For i = 1 To Sheets.Count
Sheets(i).UnProtect
Next i
End Sub
Sub SelectUnlockedCells()
Dim WorkRng As Range
Dim OutRng As Range
Dim Rng As Range
On Error Resume Next
Set WorkRng = Application.ActiveSheet.UsedRange
Application.ScreenUpdating = False
For Each Rng In WorkRng
If Rng.Locked = False Then
If OutRng.Count = 0 Then
Set OutRng = Rng
Else
Set OutRng = Union(OutRng, Rng)
End If
End If
Next
If OutRng.Count > 0 Then OutRng.Select
Application.ScreenUpdating = True
End Sub
The other functions work OK.
Consider:
Public lastsheet As String
Sub Select_Last()
lastsheet = Sheets(Sheets.Count).Name
Sheets(lastsheet).Select
End Sub
The key issue is to assign a value to a variable before using it.
I have a template sheet that I have set up named "Template".
I have a range of cells on another worksheet called "Formulation" that I would like it to look through the range "G7:W7" and create a copy of the "Template" and rename it accordingly.
I have adapted a piece of code I have found but I keep encountering a run time error 13 - type mismatch.
Here is the code:
`Sub CopyInfoSheetandInsert()
'
' CopyInfoSheetandInsert Macro
'
Dim rcell As Range
Dim Background As Worksheet
Set Background = Sheets("Formulation")
For Each rcell In Range("D7:W7")
If rcell.Value <> "" Then
Sheets("Template").Copy Before:=Sheets("COSHH")
Sheets("Template (2)").Name = rcell.Value
End If
Next rcell
End Sub
Any advice would be greatly appreciated!
UPDATE
By moving the macro button to the formulation page the copy function now works however, on the following line of code I now get a subscript out of range error?
Sheets("Template(2)").Name = rcell.Value
Kind Regards,
Aidan
You need something like:
Sub CopyInfoSheetandInsert()
Dim rcell As Range
Dim Background As Worksheet
Set Background = Sheets("Formulation")
For Each rcell In Range("D7:W7")
If rcell.Value <> "" And SheetExists(rcell.Value) = False Then
Sheets("Template").Copy Before:=Sheets("COSHH")
Sheets(Sheets("COSHH").Index - 1).Name = rcell.Value
End If
Next rcell
End Sub
Function SheetExists(SheetName) As Boolean
Dim sht As Worksheet
'Assume Failure
SheetExists = False
For Each sht In ActiveWorkbook.Sheets
If sht.Name = SheetName Then
'Success
SheetExists = True
Exit Function
End If
Next sht
End Function
I'm at a loss as to why it seems that I can store a range value in the range property of a worksheet variable but not in a range variable. I got error 91 every time I tried to run this code:
Dim ws As Worksheet, rng As Range
Set ws = Worksheets.Add
ws.name = "Potato"
rng = ws.Range("A1:K1")
rng.PasteSpecial
I was able to run the program successfully by replacing the last 2 lines with:
ws.Range("A1:K1").PasteSpecial
This works, even though it clutters other parts of my code. But I can't understand for the life of me what the problem was with using the range variable was.
I would appreciate any clarification anyone can provide.
You have to use Set with object variables:
Set rng = ws.Range("A1:K1")
Here is added code that will make sure there won't be an error if you already have a sheet named "Potato"
Code to make sure Sheet("Potato") doesn't already exist.
Sub Button1_Click()
Dim ws As Worksheet, rng As Range, cRng As Range
Dim worksh As Integer
Dim worksheetexists As Boolean
Dim s As String
s = "Potato"
Set cRng = ActiveSheet.Range("A1:K1")
worksh = Application.Sheets.Count
worksheetexists = False
For x = 1 To worksh
If Worksheets(x).Name = s Then
worksheetexists = True
MsgBox s & ", already Exists"
Exit For
End If
Next x
If worksheetexists = False Then
Set ws = Worksheets.Add()
ws.Name = s
Set rng = ws.Range("A1:K1")
cRng.Copy
rng.PasteSpecial Paste:=xlPasteAll
Application.CutCopyMode = 0
End If
End Sub
I have a main worksheet (Install_Input) where sheet number, test section, and material are manually entered by user.
(Below: illustration of Install_Input ws: Range A1:C8)
Sheet# | TestSection | Material
.....1.....|..........A..........|.STEEL.|
.....2.....|..........B..........|.PLASTIC.|
.....3.....|..........C..........|.STEEL.|
.....5.....|..........G..........|.STEEL.|
.....2.....|..........F..........|.PLASTIC.|
.....2.....|..........A..........|.STEEL.|
.....5.....|..........D..........|.PLASTIC.|
I want to generate sheets within the current workbook that correspond to sheet numbers entered in Install_Input. The code I made will generate a new sheet for each value in MyRange, however, I would like for my code to skip over generating sheets that already exist. I tried using the "On Error Resume Next" and "On Error GoTo 0" commands to solve this problem, but they just generated unnamed sheets to compensate for those that already exist.
Sub Consolidate_Sheets()
Dim MyCell As Range
Dim MyRange As Range
Dim ws As Worksheet
Set MyRange = Sheets("Install_Input").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
If Sheets(Sheets.Count).Name <> MyCell.Value Then
'On Error Resume Next
Sheets.Add After:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = MyCell.Value
'On Error GoTo 0
End If
Next MyCell
End Sub
You can use the following two functions:
Function getSheetWithDefault(name As String, Optional wb As Excel.Workbook) As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
If Not sheetExists(name, wb) Then
wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count)).name = name
End If
Set getSheetWithDefault = wb.Sheets(name)
End Function
Function sheetExists(name As String, Optional wb As Excel.Workbook) As Boolean
Dim sheet As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
sheetExists = False
For Each sheet In wb.Worksheets
If sheet.name = name Then
sheetExists = True
Exit Function
End If
Next sheet
End Function
To use it in your code:
Sub Consolidate_Sheets()
Dim MyCell As Range
Dim MyRange As Range
Dim ws As Worksheet
Set MyRange = Sheets("Install_Input").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
If Sheets(Sheets.Count).Name <> MyCell.Value Then
'On Error Resume Next
set ws = getSheetWithDefault(MyCell.Value)
'On Error GoTo 0
End If
Next MyCell
End Sub
You could implement a CheckSheet function like the one described in this SO answer that loops through all existing sheets and compares the name of each sheet with the passed-in value.
I was attempting to loop through every worksheet in every workbook in a folder and make sure only the cells containing formulas were locked. I have already been using code to lock all cells in every worksheet, and code to lock every formula in a worksheet, successfully for a few months, so I basically mashed the two pieces of code together to get this:
Sub LockAllFormulas()
Dim myOldPassword As String
Dim myNewPassword As String
Dim ws As Worksheet
Dim FileName As String
Dim rng As Range
myOldPassword = InputBox(Prompt:="Please enter the previously used password.", Title:="Old password input")
myNewPassword = InputBox(Prompt:="Please enter the new password, if any.", Title:="New password input")
FileName = Dir(CurDir() & "\" & "*.xls")
Do While FileName <> ""
Application.DisplayAlerts = False
If FileName <> "ProtectionMacro.xlsm" Then
MsgBox FileName
Workbooks.Open (CurDir & "\" & FileName)
For Each ws In ActiveWorkbook.Worksheets
If Not Cells.SpecialCells(xlCellTypeFormulas) Is Nothing Then
ActiveWorkbook.ActiveSheet.Unprotect Password:=myOldPassword
ActiveWorkbook.ActiveSheet.Cells.Locked = False
For Each rng In ws.Cells.SpecialCells(xlCellTypeFormulas)
rng.Locked = True
Next rng
ActiveWorkbook.ActiveSheet.Protect Password:=myPassword
End If
Next ws
ActiveWorkbook.Save
ActiveWorkbook.Close
End If
FileName = Dir()
Loop
Application.DisplayAlerts = True
End Sub
Every time I run it it shows a 400 error. The error matches one I got whenever the code runs into a sheet that doesn't have any code in it, but I thought I fixed that problem when I added:
If Not Cells.SpecialCells(xlCellTypeFormulas) Is Nothing Then
Any ideas what else could be going wrong?
When working with SpecialCells, you have to be very careful. What I do is I store them in a range sandwiched between OERN and then check of they are not nothing. Here is an example
Dim rng As Range
On Error Resume Next
Set rng = ws.Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If Not rng Is Nothing Then
'
'~~> Rest of the code
'
End If
Applying that to your code will be like this (UNTESTED)
Dim LockedRange As Range
For Each ws In ActiveWorkbook.Worksheets
With ws
On Error Resume Next
Set LockedRange = .Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If Not LockedRange Is Nothing Then
.Unprotect Password:=myOldPassword
.Cells.Locked = False
LockedRange.Locked = True
.Protect Password:=myPassword
End If
Set LockedRange = Nothing
End With
Next ws