I am fairly new to VBA so a lot of my code is what I have researched on the internet and put together. A bit background to what I am trying to achieve: -
I have two works books which have an identical layout. One work book is my original where the VBA code is held and the other is a type of overlay document. I have a column with a codes in the Overlay and need to search the original work book same column for this code if its found then copy entire row from overlay into the original and deleting the row found in the original, if its not found in the original just to copy row across.
The line of code I am getting the run-time error on is: -
Set rngFound = Workbooks("OverLay").Worksheets("Overlay").Range("G:G").Find(What:=r.Value, LookIn:=xlValues, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
Below is an extract of the code I am using.
Dim sht1 As Worksheet 'Current active worksheet (original version)
Dim sht2 As Worksheet 'Worksheet in OverLay
Dim rngFound As Range
Set sht2 = Workbooks("Overlay").Worksheets("Overlay")
With Workbooks("Original").Worksheets("Formatted")
lastRow = .Range("G" & .Rows.Count).End(xlUp).Row
End With
With sht2
For Each Row In .Range("G:G")
Set rngFound = Workbooks("OverLay").Worksheets("Overlay").Range("G:G").Find(What:=r.Value, LookIn:=xlValues, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
If Not rngFound Is Nothing Then
rngFound.Copy
Workbooks("Original").Worksheets("Formatted").Range(rngFound).PasteSpecial
End If
Next
End With
I'll start by showing you what's wrong:
Dim sht1 As Worksheet '// <~~ This never gets used?
Dim sht2 As Worksheet 'Worksheet in OverLay
Dim rngFound As Range
Set sht2 = Workbooks("Overlay").Worksheets("Overlay")
With Workbooks("Original").Worksheets("Formatted")
lastRow = .Range("G" & .Rows.Count).End(xlUp).Row
End With
With sht2
For Each Row In .Range("G:G")
'// 'Row' in the above line will be treated as a variant as it hasn't been declared.
'// As such, it will most likely default to a Range object, which means you are
'// actually looping through each cell in that column. The lesson here is "be explicit"
'// and make sure the code is looking at exactly what you want it to look at.
Set rngFound = Workbooks("OverLay").Worksheets("Overlay").Range("G:G").Find(What:=r.Value, LookIn:=xlValues, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
'// You've already set this sheet to 'sht2' so just use that instead. Also, as
'// we know - 'r' has not been set and so causes an error.
If Not rngFound Is Nothing Then
rngFound.Copy
Workbooks("Original").Worksheets("Formatted").Range(rngFound).PasteSpecial
'// 'rngFound' is already a range object, no need to wrap it in a Range() method.
End If
Next
End With
This can be re-written as such:
Dim originalWS As Worksheet '// give your variables meaningful names!
Dim overlayWS As Worksheet
Dim rngSearchParam As Range
Dim rngFound As Range
Set originalWS = Workbooks("Original").Sheets("Formatted")
Set overlayWS = Workbooks("Overlay").Sheets("Overlay")
With overlayWS
For Each rngSearchParam In Intersect(.Range("G:G"), .UsedRange)
Set rngFound = .Range("G:G").Find(rngSearchParam.Value, LookIn:=xlValues, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
If Not rngFound Is Nothing Then
originalWS.Range(rngFound.Address).Value = rngFound.Value
End If
Next
End With
Although it seems like your searching a column, for a value defined by a cell in the same column - so not sure what the "end goal" is here. Hopefully it clarifies the issues you've been having though
Related
I have been able to modify most of my VBA procedures to set ranges to equal other ranges to avoid copy and paste. It has speed up my code incredibly. However, there is a few cases where I can't figure out how to not use copy and paste. Below is one example:
Dim Creation2 As Worksheet
Dim HoleOpener As Worksheet
Dim Dal As Range
Dim Lad As Range
Dim Pal As Range
Dim LastRow As Long
Dim ws As Worksheet
Dim fndList As Variant
Dim rplcList As Variant
Set HoleOpener = Worksheets("HoleOpener")
LastRow = HoleOpener.Range("C" & Rows.Count).End(xlUp).Row
On Error Resume Next
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> "Creation2" And ws.Name <> "BitInfoTable" And ws.Name <> "DailyBitInfoTable" And ws.Name <> "BitRunInfoTable" And ws.Name <> "HoleOpener" Then
Set Lad = ws.Cells.Find(What:="StartCopy", LookIn:= _
xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=True, SearchFormat:=False).Resize(21, 25).Copy
Sheets("HoleOpener").Cells(Rows.Count, "A").End(xlUp).Offset(1).PasteSpecial xlPasteValues
End If
Next
When I search I can't find any examples of doing something similar to this without copy/paste.
You will simply need to swap the ranges I listed with your ranges in FoundRange and PasteRange and adjust the Resize to fit your needs ((21, 25)). You have to use .value here as well.
Here is a generic example of how to set two ranges equal to each other. Since your range is greater than one, you will need to ensure each range is of equal size in terms of rows and columns spanned.
Dim FoundRange As Range, PasteRange As Range
Set FoundRange = Range("A1:B10") 'Swap this for your found value ("Lad" in your code)
Set PasteRange = Range("C1").Resize(10, 2) 'Swap this for your destination value
PasteRange.Value = FoundRange.Value
Using the same logic as above & your code, the result will look something like this:
Dim Lad As Range, PasteRange As Range
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> "Creation2" And ws.Name <> "BitInfoTable" And ws.Name <> "DailyBitInfoTable" And ws.Name <> "BitRunInfoTable" And ws.Name <> "HoleOpener" Then
Set Lad = ws.Cells.Find(What:="StartCopy", LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True).Resize(21, 25)
Set PasteRange = Sheets("HoleOpener").Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(21, 25)
Destiation.Value = Lad.Value
End If
Next ws
Basically, you are finding a range that meets a specific criteria, copying that range and pasting it in a new range.
This is how I would solve it without using copy/paste:
Find range as you have done
Iterate all cells in range (by column or row)
Store value of each cell in a variable
Set value of destination cell to variable stored in Step 3
As you can guess, it will require a lot more code, so it really is a trade-off between spending time writing and maintaining more code or optimizing for performance
I found a code but I don't why it is not working, I'm barely new to VBA. Please help me..
What I am trying to achieve is I need to Search the day today from another wb.
Here's my complete code:
Sub Sample
Sheets("Database").Select
Dim i as Workbook
Dim c as Workbook
Set i = Workbooks("Workbook1.xlsm")
Set c = Workbooks.Open(FileName:=Range("U2").Value)
'U2 contains the link or path of the file.
ThisWorkbook.Activate
Sheets("Summary").Activate
Windows("Workbook1").Activate
Sheets("Database").Select
Workbooks(2).Activate
Sheets("Summary").Select
Dim FindString As Date
Dim Rng As Range
FindString = CLng(Date)
With Sheets("Summary").Range("A:A")
Set Rng = .Find(What:=FindString, After:=.Cells(.Cells.Count), LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlbyColumns, SearchDirection;=xlNext,MatchCase;=False)
If Not Rng Is nothing then
Application.Goto Rng, True
Else
Msgbox "Nothing Then"
End if
End with
End Sub
The other workbook that was recently opened contains Summary Sheet that has Dates on Column A:A
If you're getting a syntax error - the two parameters are defined with semi-colons ";" instead of colons ":"
SearchDirection;=xlNext,MatchCase;=False
becomes
SearchDirection:=xlNext,MatchCase:=False
Fix your syntax before testing - Use Debug | Compile
Activating and Selecting is not necessary and harmful
There are also some syntax errors
You may want to re-start from the following code:
Sub Sample
Dim dbSheet as Worksheet
Dim Rng As Range
Set dbSheet = Workbooks("Workbook1.xlsm").Sheets("Database") 'set the “database” worksheet
With Workbooks.Open(FileName:=dbSheet.Range("U2").Value).Sheets("Summary") 'open the workbook whose link is in “database” sheet cell U2 and reference its “Summary” sheet
With .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)) ' reference referenced sheet column A cells from row 1 down to last not empty row
Set Rng = .Find(What:=CLng(Date), After:=.Cells(.Rows.Count), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False) ' try finding current date starting from the cell at the top of referenced range
End With
End With
If Not Rng Is nothing then
Application.Goto Rng, True
Else
Msgbox "Nothing Then"
End if
End Sub
This code is untested but from the explanations in comments you can tweak it to reach the goal
I have an excel workbook where an unknown amount of data from text files can be imported (the user will import as many text files as they feel necessary). I am attaching an identifier (1, 2, 3, etc) each time a text file is imported to the workbook. On the "Information Sheet" I have a form control combobox where the user selects the "initial data set" aka (1, 2, 3, etc) by selecting the identifier value from the dropdown. What I want to happen is when the user selects a value to specify the initial data set, this data set will get highlighted in grey on the "Data Importation Sheet" aka the sheet where all the data gets imported to. I think my code is close but it isnt working.
Here is my code for the Combobox:
Private Sub ComboBox1_Change()
Call Find_Initial_Data_Set
End Sub
And here is my code for highlighting the data in the "Data Importation Sheet" according to the value in cell E12 where my Combobox is located:
Sub Find_Initial_Data_Set()
Dim ws As Worksheet
Dim aCell As Range
Dim aCell1, aCell2, aCell3 As Range
Dim NewRange As Range
Dim A As String
Dim LastRow As Integer
Worksheets("Information Sheet").Activate
If Range("E12").Value <> "" Then
Set ws = Worksheets("Data Importation Sheet")
A = Worksheets("Information Sheet").Range("E12").Value
Worksheets("Data Importation Sheet").Activate
With ws
Set aCell = .Rows(1).Find(What:=A, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
LastRow = Worksheets("Data Importation Sheet").Cells(Rows.Count, "A").End(xlUp).Offset(-1).Row
With ws
Set aCell1 = aCell.Offset(0, -1)
Set aCell2 = aCell.Offset(LastRow, 5)
Debug.Print aCell1.FormulaR1C1
Debug.Print aCell2.FormulaR1C1
Set NewRange = .Range(aCell1.Address & ":" & aCell2.Address)
Debug.Print NewRange.Address
End With
NewRange.Interior.ColorIndex = 15
Else
End If
End Sub
Here are some visuals of my excel book:
Data Importation Sheet where the data gets input (you cannot see the identifier in this pic but beneath the data I have a cell that says Identifier with the corresponding importation value beside it):
Information Sheet where the user selects the initial data set based on identifier:
And this is what I would like the Data Importation Sheet to look like after the user selects 1 (for example) for the initial data set:
Any advice would be greatly appreciated!
the code would be like this.
sheet's code
Private Sub ComboBox1_Change()
Call Find_Initial_Data_Set(ComboBox1.Text)
End Sub
module code
Sub Find_Initial_Data_Set(A As String)
Dim Ws As Worksheet
Dim aCell As Range, NewRange As Range
Dim LastRow As Integer
Set Ws = Worksheets("Data Importation Sheet")
With Ws
If A <> "" Then
Set aCell = .Rows(1).Find(what:=A, after:=.Range("a1"), LookIn:=xlValues, lookat:=xlPart)
If aCell Is Nothing Then
Else
Set aCell = aCell.Offset(, -1)
LastRow = .Range("a" & Rows.Count).End(xlUp).Row
Set NewRange = aCell.Resize(LastRow, 7)
NewRange.Interior.ColorIndex = 15
End If
End If
End With
End Sub
i rewrote your code somewhat
please single-step through code using F8 key
check if correct ranges are "selected" at "debug" lines
please update your post with findings
i suspect that the wrong cells are being referenced once the worksheet is partially populated
also, please refrain from using: ( ... this means, anyone reading this)
With ws
Set aCell = .Rows(1).Find(What:=A, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
use this:
Set aCell = ws.Rows(1).Find(What:=A, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
it is shorter and more readable
use "with" convention only if it really simplifies the code a lot
see the end of the code for info that may help you
Sub Find_Initial_Data_Set()
Dim infoSht As Worksheet
Dim dataImpSht As Worksheet
Dim aCell As Range
' Dim aCell1, aCell2 As Range ' do not use ... aCell1 is declared as variant. not as range
Dim aCell1 As Range, aCell2 As Range, aCell3 As Range
Dim NewRange As Range
Dim A As String
Dim LastRow As Integer
Set dataImpSht = Worksheets("Data Importation Sheet")
Set infoSht = Worksheets("Information Sheet")
A = infoSht.Range("E12").Value
If A <> "" Then
Set aCell = dataImpSht.Rows(1).Find(What:=A, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
dataImpSht.Activate ' debug .Select command fails if sheet is not visible
aCell.Select ' debug (this should highlight "aCell")
dataImpSht.Cells(dataImpSht.Rows.Count, "A").Select ' debug
dataImpSht.Cells(dataImpSht.Rows.Count, "A").End(xlUp).Select ' debug
dataImpSht.Cells(dataImpSht.Rows.Count, "A").End(xlUp).Offset(-1).Select ' debug
dataImpSht.Cells(dataImpSht.Rows.Count, "A").End(xlUp).Offset(1).Select ' debug
LastRow = dataImpSht.Cells(dataImpSht.Rows.Count, "A").End(xlUp).Offset(-1).Row
aCell.Select ' debug
aCell.Offset(0, -1).Select ' debug
aCell.Offset(LastRow, 5).Select ' debug
Set aCell1 = aCell.Offset(0, -1)
Set aCell2 = aCell.Offset(LastRow, 5)
aCell1.Select ' debug
aCell2.Select ' debug
Debug.Print aCell1.FormulaR1C1
Debug.Print aCell2.FormulaR1C1
Set NewRange = dataImpSht.Range(aCell1.Address & ":" & aCell2.Address)
NewRange.Select ' debug
Debug.Print NewRange.Address
NewRange.Interior.ColorIndex = 15
End If
'---------------------------------------------------------------------------
' check this out ... it may be what you need to use
Dim aaa As Range
Set aaa = dataImpSht.Cells(dataImpSht.Rows.Count, "A").End(xlUp).Offset(1)
aaa.Select
aaa.Range("a1").Select ' aaa can be thought off as the new top left corner
aaa.Range("b2").Select ' you can refer to cells in relation to aaa
Set aaa = aaa.Offset(4) ' and move position of aaa for each iteration
aaa.Range("a1").Select
aaa.Range("b2").Select
'---------------------------------------------------------------------------
End Sub
You need to change LastRow to the following as all you need is the row number:
LastRow = Worksheets("Data Importation Sheet").Cells(Rows.Count, "A").End(xlUp).Row - 1
I would like to ask if you can help with the code below. On every sheet in my workbook there is the same kind of a table, however on each sheet the table has different location and values. I need to go through all sheets, search for table values on every sheet and then do some other operations with the values. I use Find function to determine header of the table and subsequently table range. The Find function does not work properly though as it keeps found address of "Header" cell from the first sheet for every other sheet. Is there any way to reset the found header address value before looping to another sheet? Thank you in advance.
Sub FindInDynamicRanges()
Dim wb1 As Workbook
Dim ws As Worksheet
Dim FoundCell, FoundTab, TabEntries As Excel.Range
Dim FirstAddr As String
Dim FirstRow, LastRow As Long
Set wb1 = ThisWorkbook
'Find all occurences of any table value on all sheets in dynamic ranges
For Each ws In wb1.Worksheets
Set ws = ActiveSheet
'Find "Header" cell
Set FoundCell = ws.Columns(2).Find(What:="Header", LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
MsgBox FoundCell.Address
'Set number of first entry row and last entry row
FirstRow = FoundCell.Row + 1
LastRow = ws.Cells.Find(What:="*", After:=Range("A1"), LookAt:=xlPart, LookIn:=xlValues, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False).Row
ws.Range("B" & FirstRow & ":B" & LastRow).Name = "TabEntries"
MsgBox Range("TabEntries").Address
With ws.Range("TabEntries")
Set LastCell = .Cells(.Cells.Count)
End With
Set FoundTab = ws.Range("TabEntries").Find(What:="*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not FoundTab Is Nothing Then
FirstAddr = FoundTab.Address
End If
Do Until FoundTab Is Nothing
'do some staff with found values
Set FoundTab = ws.Range("TabEntries").FindNext(After:=FoundTab)
If FoundTab.Address = FirstAddr Then
Exit Do
End If
Loop
Next ws
End Sub
as it keeps found address of "Header" cell from the first sheet for every other sheet.
That is because you are telling it to...
For Each ws In wb1.Worksheets
Set ws = ActiveSheet
You don't need that Set ws = ActiveSheet
When you say For Each ws, the ws is automatically initialized. So just remove the second line.
Haha! I'll stump you yet Excel gurus. ;-D
I want to set a selection range boundary based on some delimiting text.
If I do Cells.Find the entire worksheet is searched and the multiple instances are found.
The one I want found is likely the 3rd or 4th instance of the delimiter. Actually it is in a specific column, B. However it is a non-contiguous range and the actual search start in the column is a few hundred cells down.
How do I search within that column and set my reusable range begin variable, set to the delimiter cell (not including the delimiter cell)?
I've tried this:
Dim selectionStart As Range, selectionEnd As Range
Dim currentCell As Range, dataRange As Range
Dim lastRow As Range, insertRows As Range, destinationCell As Range
Range("b1", Range("b65536").End(xlUp)).Select
Set selectionStart = Selection.Find(What:="<-RANGE START->", After:=ActiveCell, LookIn _
:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False)
It selects the range but doesn't set the variable.
I'm trying out all these things annoyingly big so I can see what they do without too much eyestrain. Elegance is not required.
TIA
Do you mean this?
Option Explicit
Sub test()
Dim selectionStart As Range, selectionEnd As Range
Dim currentCell As Range, dataRange As Range
Dim lastRow As Range, insertRows As Range, destinationCell As Range
Dim rngtoSearch As Range
Dim foundValue As Variant
Dim foundAddress As String
Dim foundRow As Long
With sheetWhatever 'change to whatever sheet codename required
Set rngtoSearch = .Range("b1", .Range("b65536").End(xlUp))
Set selectionStart = rngtoSearch.Find(What:="<-RANGE START->", LookIn _
:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False)
'check it actually found a range
If Not selectionStart Is Nothing Then
'If found set the variable
foundValue = selectionStart.Value 'set as value
foundAddress = selectionStart.Address 'set as address string
foundRow = selectionStart.Row ' set as row
End If
End With
End Sub