I've been fighting with this for a couple days now, and am at a loss on what else to try. My goal is to have a prompt for where a workbook is saved, this spreadsheet is obtained from an external source and name/location can vary. After opening the workbook, switch over to the second sheet and start searching for the values to copy to the workbook the macro is run out of.
The code I have works great if I set a breakpoint at the calculation for the last row, and at the For loop. Without those 2 breakpoints, it appears that none of the information in the workbook loads before running the rest of the code.
Public Sub Clm2Count()
Dim i, j, k, last As Long
Dim wkbSource, wkbCrnt As Workbook
Dim str As Variant
Dim strArray()
strArray() = Array("THIS", "IS", "MY", "ARRAY")
Set wkbCrnt = ThisWorkbook
k = 1
With Application.FileDialog(msoFileDialogOpen)
.Filters.Clear
.Filters.Add "Excel 2007-13", "*.xlsx; *.xlsm; *.xlsa"
.AllowMultiSelect = False
.Show
If .SelectedItems.Count > 0 Then
Application.ScreenUpdating = False
Set wkbSource = Workbooks.Open(.SelectedItems(1))
Application.ScreenUpdating = True
End If
End With
Sheets(2).Activate
Cells(5,1).Select 'Trying to activate a cell before calculating last, didn't work
last = wkbSource.Sheets(2).Cells(wkbSource.Sheets(2).Cells.Rows.Count, 1).End(xlUp).Row
For i = 51 To last
If InStr(1, wkbSource.Sheets(2).Cells(i, 2).Value, "TEST") > 0 Then
For Each str In strArray
If InStr(1, wkbSource.Sheets(2).Cells(i, 2).Text, str, vbTextCompare) > 0 Then
If InStr(1, wkbSource.Sheets(2).Cells(i, 2).Text, "A", vbTextCompare) > 0 Or InStr(1, Cells(i, 2).Text, "B", vbTextCompare) > 0 Then
If str = "MY" Then 'Specific value from the array
wkbSource.Sheets(2).Cells(i, 3).Copy
wkbCrnt.Sheets(1).Cells(k, 1).PasteSpecial
wkbCrnt.Sheets(1).Cells(k, 2).Value = "QC"
wkbCrnt.Sheets(1).Cells(k, 3).Value = i & ", " & str
k = k + 1
Exit For
End If
ElseIf InStr(1, wkbSource.Sheets(2).Cells(i, 2).Text, "C", vbTextCompare) > 0 Then
wkbSource.Sheets(2).Cells(i, 3).Copy
wkbCrnt.Sheets(1).Cells(k, 1).PasteSpecial
wkbCrnt.Sheets(1).Cells(k, 2).Value = "QC"
wkbCrnt.Sheets(1).Cells(k, 3).Value = i & ", " & str
k = k + 1
Exit For
Else
Exit For
End If
End If
Next str
wkbSource.Activate
End If
Next i
End Sub
Any ideas on what I might be overlooking?
Edit:
Here are images of the beginning and end of column A, with the identifiers removed
Beginning
End
Instead of Sheets(2).Activate use wkbSource.Sheets(2).Activate. Same goes for cells and any other kind of ranges you are using.
It is especially crucial to be as explicit as possible which is the target workbook when you have a multiple workbook interaction.
To find last row use this line:
last = wkbSource.Sheets(2).Cells(wkbSource.Sheets(2).Cells.Rows.Count, 1).End(xlUp).Row
Edit: The issue was caused by a hidden sheet - it is better to use sheets name in this case.
Related
I'm having a bit of trouble with this and I'm not sure why...
My code (such that it is, a work in progress) is getting stuck on this line:
Set starRange = .Range(Cells(title), Cells(LR, 3))
Can I not use a range variable to set a new range in this way?
Sub cellPainter()
Dim ws As Worksheet
Dim starRange, titleRange, found As Range
Dim errorList() As String
Dim i, LR As Integer
i = 0
ReDim errorList(i)
errorList(i) = ""
For Each ws In ActiveWorkbook.Worksheets
With ws
LR = .Cells(.Rows.Count, "C").End(xlUp).Row
Set titleRange = .Range("C4")
If InStr(1, titleRange, "Title", vbBinaryCompare) < 1 Then
Set found = .Range("C:C").Find("Title", LookIn:=xlValues)
If Not found Is Nothing Then
titleRange = found
Else
errorList(i) = ws.Name
i = i + 1
ReDim Preserve errorList(i)
End If
End If
Set starRange = .Range(Cells(titleRange), Cells(LR, 3))
For Each cell In starRange
If InStr(1, cell, "*", vbTextCompare) > 0 Then Range(cell, cell.Offset(0, 2)).Interior.ColorIndex = 40
If InStr(1, cell, "*", vbTextCompare) = 0 Then Range(cell, cell.Offset(0, 2)).Interior.ColorIndex = 0
Next cell
End With
Next ws
If errorList(0) <> "" Then
txt = MsgBox("The following worksheets were missing the Title row, and no colour changes could be made:" & vbNewLine)
For j = 0 To i
txt = txt & vbCrLf & errorList(j)
Next j
MsgBox txt
End If
End Sub
Edit:
Rory cracked it!
When using a variable inside Range, the Cells property is not required:
Set starRange = .Range(titleRange, .Cells(LR, 3))
I'm running this macro that auto scrolls at designated time intervals. I'm having two issues:
When the macro finishes, I want it to return to the top of the sheet, but it doesn't.
I want it to run only on designated sheets, not across the entire workbook.
What's wrong with my code?
Sub ReRunMacro()
Dim xMin As String
Dim lastRow As Long, i As Long
Dim ws As Worksheet
ws = ThisWorkbook.Worksheets("CNC Machining Cell 2", "CNC Grinding Cell", "CNC Turning Cell 1 & 3", "CNC Turning Cell 2")
lastRow = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To 14 Step 2
Cells(i, 1).Select
ActiveWindow.SmallScroll down:=1
Application.Wait (Now + TimeValue("0:00:03"))
If i = lastRow - 2 Or i = lastRow - 1 Then
i = 0
Cells(1, 1).Select
End If
Next i
Debug.Print (i)
xMin = GetSetting(AppName:="Kutools", Section:="Macro", Key:="min", Default:="")
If (xMin = "") Or (xMin = "False") Then
xMin = Application.InputBox(prompt:="Please input the interval time you need to repeat the Macro", Title:="Kutools for Excel", Type:=2)
SaveSetting "Kutools", "Macro", "min", xMin
End If
If (xMin <> "") And (xMin <> False) Then
Application.OnTime Now() + TimeValue("0:" + xMin + ":0"), "ReRunMacro"
Else
Exit Sub
End If
End Sub
Here you go, I've explained how it works in the code comments
Sub ReRunMacro()
Dim xMin As String
Dim lastRow As Long, i As Long
Dim ws As Worksheet
Dim validSheets() As Variant
Set ws = ActiveSheet
' put the sheet names you want visible when the code is running into an array
validSheets = Array("CNC Machining Cell 2", "CNC Grinding Cell", "CNC Turning Cell 1 & 3", "CNC Turning Cell 2")
' check were on one of those sheets, if not exit (or pause the code, whatever you want to do
If UBound(Filter(validSheets, ws.Name)) = -1 Then ' we're not on the right sheet
Exit Sub ' you can use the worksheet selection event to run this code again when the user moves to a different sheet
End If
lastRow = ws.Range("A100000").End(xlUp).Row ' it's best not to use row count, its unreliable, also you were going from the last row up and could land on row 1
For i = 1 To 14 Step 2
ws.Cells(i, 1).Select ' always best to prefix a range with the worksheet it's on
ActiveWindow.SmallScroll down:=1
Application.Wait (Now + TimeValue("0:00:03"))
If i = lastRow - 2 Or i = lastRow - 1 Then
i = 0
ws.Cells(1, 1).Select
End If
Next i
xMin = GetSetting(AppName:="Kutools", Section:="Macro", Key:="min", Default:="")
If (xMin = "") Or (xMin = "False") Then
xMin = Application.InputBox(prompt:="Please input the interval time you need to repeat the Macro", Title:="Kutools for Excel", Type:=2)
SaveSetting "Kutools", "Macro", "min", xMin
End If
If (xMin <> "") And (xMin <> False) Then
Application.OnTime Now() + TimeValue("0:" + xMin + ":0"), "ReRunMacro"
Else
MsgBox "No values supplied, code will end", vbInformation ' it's polite to inform people you're stopping the code
Exit Sub
End If
End Sub
I've written code to loop through a folder of workbooks and extract certain columns from their worksheets then paste the data onto a single worksheet
This code was working well until the 29th workbook, where the data that I wanted pasted at the bottom of my ExtractedColumns worksheet was instead pasted at the top. The same happened for the remaining workbooks- it overwrites the data that is at the top.
This problem occurs after 60,000 rows have been pasted into the ExtractedColumns worksheet, which is well below the limit on row numbers for an Excel worksheet.
I can't figure out why this is happening, especially because it's working fine for the first 28 workbooks.
Here's my code for copying and pasting (I'm not posting the code to loop through the folder and open each workbook, because I feel like that code isn't causing the problem):
Sub extract()
Dim curr As Range
Dim cell As Range
Dim lastRow As Variant
Dim n As Long
Dim found As Boolean
Dim FirstRow As Range
Dim wbOpen As Object
found = False
Set wbOpen = Workbooks("ExtractedColumns")
'finds where data starts
For i = 3 To 50
If Not IsEmpty(Cells(i, "E")) Then
Exit For
End If
Next
' Next
'Par B name: if there is a header with one of these names, then it extracts those
For Each curr In Range("A" & i, "Z" & i)
If InStr(1, curr.Value, "Protein name", vbTextCompare) > 0 Or InStr(1, curr.Value, "description", vbTextCompare) > 0 Or InStr(1, curr.Value, "Common name", vbTextCompare) > 0 Then
lastRow = wbOpen.Sheets("Sheet1").Cells(Rows.Count, "D").End(xlUp).Row
Range(curr.Offset(1), Cells(Rows.Count, curr.Column).End(xlUp)).Copy Destination:=wbOpen.Sheets("Sheet1").Range("D" & lastRow + 1)
found = True
Exit For
End If
Next
'If there isn't a header with one of the above names, then see if there is one with the name "protein"
If Not found Then
For Each curr In Range("A" & i, "Z" & i)
If InStr(1, curr.Value, "protein", vbTextCompare) > 0 Then
lastRow = wbOpen.Sheets("Sheet1").Cells(Rows.Count, "D").End(xlUp).Row
Range(curr.Offset(1), Cells(Rows.Count, curr.Column).End(xlUp)).Copy Destination:=wbOpen.Sheets("Sheet1").Range("D" & lastRow + 1)
Exit For
End If
Next
End If
'Par B accession
For Each curr In Range("A" & i, "Z" & i)
If InStr(1, curr.Value, "accession", vbTextCompare) > 0 Or InStr(1, curr.Value, "Uniprot", vbTextCompare) > 0 Or InStr(1, curr.Value, "IPI") > 0 Then
lastRow = wbOpen.Sheets("Sheet1").Cells(Rows.Count, "E").End(xlUp).Row
Range(curr.Offset(1), Cells(Rows.Count, curr.Column).End(xlUp)).Copy Destination:=wbOpen.Sheets("Sheet1").Range("E" & lastRow + 1)
found = True
Exit For
End If
Next
'Par B site
For Each curr In Range("A" & i, "Z" & i)
If (UCase(curr.Value) = "RESIDUE" Or UCase(curr.Value) = "POSITION" Or UCase(curr.Value) = "POSITIONS" Or InStr(1, curr.Value, "Positions within protein", vbTextCompare) > 0 Or InStr(1, curr.Value, "Position in peptide", vbTextCompare) Or InStr(1, curr.Value, "Site", vbTextCompare) > 0) And (InStr(1, curr.Value, "modification", vbTextCompare) = 0 And InStr(1, curr.Value, "ERK") = 0 And InStr(1, curr.Value, "class", vbTextCompare) = 0) Then
lastRow = wbOpen.Sheets("Sheet1").Cells(Rows.Count, "G").End(xlUp).Row
Range(curr.Offset(1), Cells(Rows.Count, curr.Column).End(xlUp)).Copy Destination:=wbOpen.Sheets("Sheet1").Range("G" & lastRow + 1)
Exit For
End If
Next
'puts dashes in any blank cells in the columns (so spreadsheet isn't ragged)
n = wbOpen.Sheets("Sheet1").UsedRange.Rows(wbOpen.Sheets("Sheet1").UsedRange.Rows.Count).Row
For Each curr In wbOpen.Sheets("Sheet1").Range("D2:D" & n)
If curr.Value = "" Then curr.Value = " - "
Next
For Each curr In wbOpen.Sheets("Sheet1").Range("E2:E" & n)
If curr.Value = "" Then curr.Value = " - "
Next
For Each curr In wbOpen.Sheets("Sheet1").Range("G2:G" & n)
If curr.Value = "" Then curr.Value = " - "
Next
'puts "x" in first empty row (filename will go in column A in this row)
n = wbOpen.Sheets("Sheet1").UsedRange.Rows(wbOpen.Sheets("Sheet1").UsedRange.Rows.Count + 1).Row
For Each curr In wbOpen.Sheets("Sheet1").Range("D2:D" & n)
If curr.Value = "" Then curr.Value = "x"
Next
For Each curr In wbOpen.Sheets("Sheet1").Range("E2:E" & n)
If curr.Value = "" Then curr.Value = "x"
Next
For Each curr In wbOpen.Sheets("Sheet1").Range("G2:G" & n)
If curr.Value = "" Then curr.Value = "x"
Next
End Sub
If you are opening up some old format workbooks (which have a limit of 65536 rows) then your unqualified Rows.Count in
lastRow = wbOpen.Sheets("Sheet1").Cells(Rows.Count, "D").End(xlUp).Row
is making that line equivalent to
lastRow = wbOpen.Sheets("Sheet1").Cells(65536, "D").End(xlUp).Row
So, once you have more than 65536 rows in your "ExtractedColumns" worksheet, the End(xlUp) is moving all the way up to the top of the file and probably setting lastRow to 1 (unless you have some empty cells below row 1 in column D).
That line should be
lastRow = wbOpen.Sheets("Sheet1").Cells(wbOpen.Sheets("Sheet1").Rows.Count, "D").End(xlUp).Row
Always qualify Range, Cells, Rows, etc, unless you know that you want to refer to the ActiveSheet.
The project: It deals with very sensitive HR/performance data, and I need to send 1000s of employees' data to their individual managers (about 100 managers who can only see their team's data, and no one else's), so I need about 100 files split (1 for each manager).
The file: - Many different tabs, separated by role. - First column is a unique identifier made by concatenating the Manager's name with the job title ex. John Stevens_Office Manager
The task: John Stevens will have team members in many different job roles, and needs all that data in one file, separated into tabs by job role.
Based on that sample data, the ideal macro would give me 3 files with 3 worksheets in each, and 1 row of data in each worksheet. However, I will settle for the worksheet being split into multiple files.
Here is my code.
Sub SplitWB()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
ActiveWorkbook.Save
Dim OutputFolderName As String
OutputFolderName = ""
Set myDlg = Application.FileDialog(msoFileDialogFolderPicker)
myDlg.AllowMultiSelect = False
myDlg.Title = "Select Output Folder for Touchstone Files:"
If myDlg.Show = -1 Then OutputFolderName = myDlg.SelectedItems(1) & "\" Else Exit Sub
Set myDlg = Nothing
Application.CutCopyMode = False
'''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''
Dim d As Object, c As Range, k, tmp As String, unique(500)
i = 0
With ActiveSheet
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Set d = CreateObject("scripting.dictionary")
For Each c In Range(Cells(1, 1), Cells(lastRow, 1))
tmp = Trim(c.Value)
If Len(tmp) > 0 Then d(tmp) = d(tmp) + 1
Next c
For Each k In d.keys
Debug.Print k, d(k)
i = i + 1
unique(i) = k
Next k
UniqueCount = i
'start deleting
For i = 1 To UniqueCount
'Actions for new workbook
wpath = Application.ActiveWorkbook.FullName
wbook = ActiveWorkbook.Name
wsheet = ActiveSheet.Name
ActiveWorkbook.SaveAs Filename:=OutputFolderName & unique(i), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
For j = 1 To lastRow
If Range("A" & j) <> "" And Range("A" & j) <> unique(i) Then
Rows(j).Delete
j = j - 1
End If
Next
'hide helper columns
' If HideC = False And DeleteC = True Then
Columns("A:D").Hidden = True
' End If
'
Range("E8").Select
'Select Instructions tab
'Worksheets("Guidelines").Activate
'Save new workbook
ActiveWorkbook.Close SaveChanges:=True
Workbooks.Open (wpath)
'ActiveWorkbook.Close False
Workbooks(wbook).Activate
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
MsgBox ("Macro has completed successfully!" & vbNewLine & vbNewLine & "Generated files can be found in the following directory:" & vbNewLine & OutputFolderName)
End Sub
The code hangs at "If Range("A" & j) <> "" And Range("A" & j) <> unique(i) Then"
It is located about half-way into the code and the chunk starts with "For j = 1 To lastRow"
Please help. It will literally save me a day's work or more. Thanks!!!
Try
For j = lastRow to 1 step -1
If Range("A" & j) <> "" And Range("A" & j) <> unique(i) Then
Rows(j).Delete
End If
Next
instead. This allows you to loop backwards so when you delete a row it won't lose track of where you want j to be in the loop.
I have five worksheet in all that are using the below code which is stored in a workbook. The first worksheet works perfectly well with the code. The second spreadsheet can check for the first item before returning the error. The subsequent third and fourth worksheet return the error immediately. The fifth worksheet on the other hand return error 400. May I know is my code the source of the problem or it's the checkbox because I copied and paste from the first worksheet.
Sub test5()
Dim MyFile As String
Dim FinalRow As Long
Dim Row As Long
Dim i As Integer
Dim d As Integer
d = 2
i = 0
FinalRow = Cells(Rows.count, "S").End(xlUp).Row
For Row = 3 To FinalRow
If Not IsEmpty(ActiveSheet.Cells(Row, "S")) Then
i = i + 1
d = d + 1
MyFile = ActiveSheet.Cells(Row, "S").Value
If Dir(MyFile) <> "" Then
ActiveSheet.OLEObjects("CheckBox" & i). _
Object.Value = True ' <~~~~~~~~~~~~~~~~ Error occurs here
With ActiveSheet.Cells(d, "F")
.Value = Now
.NumberFormat = "dd/mm/yy"
'If (ActiveSheet.Cells(d, "F") - ActiveSheet.Cells(d, "G") >= 0) Then
' ActiveSheet.Cells(d, "F").Font.Color = vbRed
'End If
If (.Value - .Offset(0, 1).Value) >= 0 Then
.Font.Color = vbRed
Else
.Font.Color = vbBlack
End If
End With
' i = i + 1
'd = d + 1
End If
End If
Next
End Sub
The program terminates after stepping into this line of code:
ActiveSheet.OLEObjects("CheckBox" & i). _ Object.Value = True
OLEObject does not have a member called value. If you are trying to display the OLEObject, use visible instead
ActiveSheet.OLEObjects("CheckBox" & i).Visible = True
See all OLEObject members here :
OLEObject Object Members