Cells.Find and referencing another Workbook - excel

I have two Workbooks. I need to take a String from WB1 (I iterate through Column C in WB1, not every cell contains a String, but when a cell contains a string this is the one I want to copy), find it in WB2 and replace it with another String from WB1 (in the same row, but column A). Here is what I have so far:
' Checks if a given File is already open
Public Function FileInUse(sFileName) As Boolean
On Error Resume Next
Open sFileName For Binary Access Read Lock Read As #1
Close #1
FileInUse = IIf(Err.Number > 0, True, False)
On Error GoTo 0
End Function
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curCell As Range
Dim oldName As Range
Dim result As Range
' turn off screen refresh, recalculate formula to speed up sub
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' For i = 2 To Rows.Count
For i = 2 To 5
fileName = "C:\Users...\" & Workbooks("Ressources calculation.xlsm").Worksheets("Tests costs").Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
destSH.Activate
End If
Set curCell = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 3)
Set oldName = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 1)
If Not IsEmpty(curCell) Then
curCell.Copy
Set result = destWB.Sheets("Qualification Matrix").Cells.Find(What:=oldName.Text, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True, MatchByte:=True)
If Not result Is Nothing Then
result.PasteSpecial
End If
End If
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
I have added a MsgBox in the "If Not result" clause which never triggers, so I guess it is not finding the cell. It seems to extract the strings I need to use (in curCell and oldName) fine though (checked also with MsgBox). The cells in which it should search and replace are merged cells, if that makes a difference. I also tried out different values for Cells.Find (leaving all optional parameters, tried all possibilities for lookIn and lookat, MatchByte, tried oldName.Value instead).
This is the first time I'm doing something with Excel Macros/VBA, the last few hours were spend with a lot of trial and error without any result. So I'm sure what I have so far is far from optimal, but I hope that someone can help me with it.
Edit: I narrowed it down a bit. I now activate destSH right before Cells.Find and tried just using a hardcoded example String as a parameter, which works. So I guess the problem is not the find statement but how I try to extract the information I'm looking for with find.
Edit2: As requested, here is a short example walkthrough:
I have a Workbook called "Ressources calculation.xlsm" with three Columns: Current name, File name, New name. Row 4 looks like this:
Misspelledd [File name].xlsx Misspelled
Not every Cell in Column C is filled out. What I'm trying to do is: Iterate through every cell in Column C, if it is not empty copy the string which is in the same row but in Column A, look for it in the file which is noted in Column B and replace it with the right name written under Column C.
Here is a picture of the cell in the destination Workbook which should be found and the text replaced as explained above. It is a merged cell, stretching over rows 2-5.
Edit 3: I finally found out what the problem was. There were "invisible" line breaks at the end of some cells (not really invisible, but you don't easily see them since there are no characters coming after). If this is not the case, the code works.

Try something like this (added some debug.print for troubleshooting)
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curName, oldName
Dim result As Range
Dim wbRes As Workbook, wsTests As Worksheet
Set wbRes = Workbooks("Ressources calculation.xlsm") 'ThisWorkbook ?
Set wsTests = wbRes.Worksheets("Tests costs")
For i = 2 To 5
fileName = "C:\Users...\" & wsTests.Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
curName = Trim(wsTests.Cells(i, 3).Value) '<< always worth adding Trim()...
oldName = Trim(wsTests.Cells(i, 1).Value)
If Len(curName) > 0 Then
Debug.Print "Looking for: '" & oldName & _
"' on sheet '" & destSH.Name & "' in " & _
destWB.FullName
Set result = destSH.UsedRange.Find(What:=oldName, _
LookIn:=xlValues, _
LookAt:=xlWhole)
If Not result Is Nothing Then
Debug.Print "...found"
result.Value = curName
Else
Debug.Print "... not found"
End If
End If
End If 'file not in use
Next i
End Sub

Related

Copy non adjacent data cells into one workbook

this is the code that i am currently using right now, but its not enough to meet my objectives and i am stuck on how to continue....
So this code will copy the specified data from many other excel workbook in the form of xlsx into a main excel workbook and before that it will scan through the folder which contains all the different data files and the main file(all files supposed to be transfered here in a table form) e.g. Test3.xlsx,Test4.xlsx,Test.xlxs and Main.xlsm in the folder of ScanFiles. so everytime a new files comes into the folder, it will automatically update the main workbook by opening the data workbooks then copy the required data and paste it on the main workbook upon clicking a button.
Sub ScanFiles()
Dim myFile As String, path As String
Dim erow As Long, col As Long
path = "c:\Scanfiles\"
myFile = Dir(path & "*.xlsx")
Application.ScreenUpdating = False
Do While myFile <> ""
Workbooks.Open (path & myFile)
Windows(myFile).Activate
Set copyrange = Sheets("sheet1").Range("A18,B18,C18,D18,A19,B19,C19,D19")
Windows("master-wbk.xlsm").Activate
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
col = 1
For Each cel In copyrange
cel.Copy
Cells(erow, col).PasteSpecial xlPasteValues
col = col + 1
Next
Windows(myFile).Close savechanges:=False
myFile = Dir()
Loop
Range("A:E").EntireColumn.AutoFit
Application.ScreenUpdating = True
End Sub
Objectives: 1st:orignal type of file is in "file" not xlsx, so hope to find a way to open the file in xlsx format automatically before start of copying data.
2nd: requires 3 types of specified data e.g. name,surname(both of them are in fixed position always in A18 to D18 and A19 to D19 , 3rd one is to find the date, however the date is almost always in different positions in the data sheet, so i hope to add on a part to the code that makes it search for something like "ended 20190808" it will always start with ended but will always be in diff rows or even columns. i also need to arrange the data according to the date from newest(top) to oldest(bottom) and state the month of the date in words instead of numbers e.g. june
Deeply Appreciate any form of help but if possible the small section of code that can add on to my coding will make it a lot easier because im tasked to do this in a very limited amount of time
Thank you!!!
Here's some code that does similar things to what you describe. The animated .gif shows it working by stepping through the code. First the 2 data (.xlsx) files are shown so you have an idea of their content. Each is located in the same folder as the main workbook and has data in column A. Then as we step through the code each file is opened, its data manipulated (row 3 is deleted) and transferred into adjacent columns of the main workbook. The code is not limited to .xlsx files and will work with text files as well, as long as ext is defined.
Hopefully, once you understand how this works you can modify it to apply it to your case.
Option Explicit
Sub CombineFiles()
Dim theDir As String, numFiles As Integer
Dim sh As Worksheet, wk As Workbook, newSheet As Worksheet
Dim newColumn As Range, r As Range, s As String
Const ext = ".xlsx"
Err.Clear
theDir = ThisWorkbook.Path
Set newSheet = ThisWorkbook.Sheets.Add
newSheet.Name = "Combined"
Set newColumn = newSheet.Range("A1")
'Loop through all files in directory
s = Dir(theDir & "\*" & ext)
While s <> ""
numFiles = numFiles + 1
On Error Resume Next
Set wk = Workbooks.Open(theDir & "\" & s)
Set sh = ActiveSheet
sh.Rows(3).Delete Shift:=xlUp
Set r = Range("A1")
Range(r, r.End(xlDown)).Copy
newSheet.Activate
newColumn.Offset(0, numFiles) = wk.Name
newColumn.Offset(1, numFiles).Select
newSheet.Paste
Application.DisplayAlerts = False
wk.Close False
Application.DisplayAlerts = True
s = Dir()
Wend
MsgBox (numFiles & " files were processed.")
End Sub
For copy/paste of pictures see examples on this or this page. To find the last cell containing data in a column see this page; note that one example involves using the .find command. More generally, to learn how to use .find in vba, use the macro recorder and then adjust the resulting code.

Copy columns to new workbook and save as csv

I'm trying to:
Copy data (columns A and B) from one workbook (data.xlsx).
Paste into a new workbook (as values).
Save as CSV with a filename taken from column A in a third workbook (URLs.xlsx).
Process to repeat, taking the same data (which is randomised every time it is pasted) from data.xlsx and pasted into a new CSV - there are 200 rows in URLs.xlsx and so we should end up with 200 files.
I've read lots of topics, here are two I found:
Excel VBA Copy a Range into a New Workbook
https://www.excelcampus.com/vba/copy-paste-another-workbook/
What I've tried
Copying code and replacing the relevant components from various different articles across the web. Some of them work, but when I add the missing bits, I run into errors I don't understand.
Well here is an example avoiding copy pasting in new workbooks:
Expected input like:
Data.xlsx range A1:B200 with RANDBETWEEN() function:
URLs.xlsx range A1:A200 with some URL like so:
Run this code (will take approximately 1 second on my machine, tested with timer):
Dim wbData As Workbook, WBurls As Workbook
Dim CSVFileDir As String, CSVVal As String
Dim A As Long, X As Long, Y As Long, Z As Long
Option Explicit
Sub Transfer2CSV()
Set wbData = Workbooks("data.xlsx") 'Make sure it is open upon running macro
Set WBurls = Workbooks("URLs.xlsx") 'Make sure it is open upon running macro
For X = 1 To 200 'Looping through the 200 rows of WBurls
CSVFileDir = "C:\YourDrive\" & WBurls.Sheets(1).Cells(X, 1).Value & ".csv"
CSVVal = ""
A = FreeFile
Open CSVFileDir For Output As #A
With wbData.Sheets(1).Range("A1:B200") ' or whichever range you using here
.Calculate 'Randomize your range again
For Y = 1 To 200 'or however many rows you have in column A and B.
For Z = 1 To 2
CSVVal = CSVVal & .Cells(Y, Z).Value & ","
Next Z
Print #A, Left(CSVVal, Len(CSVVal) - 2)
CSVVal = ""
Next Y
End With
Close #A
Next X
End Sub
Output:
With each file looking like:
This should work. Make sure your data and URLS workbooks are open.
Sub Macro1()
Dim wsData As Worksheet, wsUrl As Worksheet, wbNew as Workbook
Dim CSVDir as String, rngU As Range
Set wsData = Workbooks("data.xlsx").Worksheets(1)
Set wsUrl = Workbooks("URLs.xlsx").Worksheets(1)
Set rngU = wsUrl.Range("A1", wsUrl.Range("A" & wsUrl.Rows.Count).End(xlUp))
CSVDir = "C:\Users\thomas.mcerlean\Desktop\Work\" 'you gave this as your dir
Set wbNew = Workbooks.Add
For Each cell In rngU
wsData.Range("A1", wsData.Range("B" & wsData.Rows.Count).End(xlUp)).Copy Destination:= wbNew.Worksheets(1).Range("A1")
wbNew.SaveAs Filename:= CSVDir & cell.Value & ".csv", FileFormat:=xlCSV
Next cell
wbNew.Close SaveChanges:=False
End Sub

cannot retrieve column titles from another workbook

I am trying to copy the string values(column titles) from another workbook in row 4 as captions for checkboxes in the workbook where I am running the code. This is what I have so far and it is not working because it is showing the error message "Subscript out of range, run time error 9" Here is what I have. After the error message pops up the line marked below is highlighted. Can anybody help me please. Thank you very much.
Function CallFunction(SheetName As Variant) As Long
Dim text As String
Dim titles(200) As String ' Dim titles(200) As String ' Array
Dim nTitles As Integer
Dim wks As Worksheet
Dim myCaption As String
PathName = Range("F22").Value
Filename = Range("F23").Value
TabName = Range("F24").Value
ControlFile = ActiveWorkbook.Name
Workbooks.Open Filename:=PathName & "\" & Filename
ActiveSheet.Name = TabName
Set wks = Workbooks("Filename").Worksheets(SheetName).Activate ' <= Highlights this line ****
For i = 1 To 199
If Trim(wks.Cells(4, i).Value) = "" Then
nTitles = i - 1
Exit For
End If
titles(i - 1) = wks.Cells(4, i).Value
Next
i = 1
For Each cell In Range(Sheets("Sheet1").Cells(4, 1), Sheets("Sheet1").Cells(4, 1 + nTitles))
myCaption = Sheets("Sheet1").Cells(4, i).Value
With Sheets("Sheet1").checkBoxes.Add(cell.Left, _
cell.Top, cell.Width, cell.Height)
.Interior.ColorIndex = 12
.Caption = myCaption
.Characters.text = myCaption
.Border.Weight = xlThin
.Name = myCaption
End With
i = i + 1
Next
End Function
Subscript out-of-range typically indicates that a specified Worksheet does not exist in the workbooks Worksheets collection.
Otherwise, are you sure that the workbook specified by FileName is already open? If not, that will raise the same error.
Ensure that A) the file is already open (or use the Workbooks.Open method to open it), and B) ensure that such a worksheet already exists (if not, you will need to create it before you can reference it by name).
Update
You have Workbooks("FileName") where "Filename" is a string literal. Try changing it to simply Filename (without the quotation marks) (this seems like the OBVIOUS error).
Also worth checking:
I also observe this line:
ActiveSheet.Name = TabName
If the sheet named by SheetName is active when the workbook opens, then that line will effectively rename it, so you will not be able to refer to it by SheetName, but instead you would have to refer to it by Worksheets(TabName). ALternatively, flip the two lines so that you activate prior to renaming:
Set wks = Workbooks(Filename).Worksheets(SheetName).Activate
ActiveSheet.Name = TabName
For further reading: avoid using Activate/Select methods, they are confusing and make your code harder to interpret and maintain:
How to avoid using Select in Excel VBA macros
If that is the case, then you could do simply:
Workbooks(Filename).Worksheets(SheetName).Name = TabName

Renaming named ranges

I have a workbook with many named ranges to rename. I have a spreadsheet with the old names and the new names.
This works:
Dim strOldName As String
Dim strNewName As String
strOldName = rngNamedRanges.Cells(1, 6).Value2
strNewName = strOldName & "_Renamed"
With ActiveWorkbook.Names(strOldName)
.Name = strNewName
End With
This does not:
Dim strOldName As String
Dim strNewName As String
strOldName = rngNamedRanges.Cells(1, 6).Value2
strNewName = CStr(rngNamedRanges.Cells(1, 8).Value2)
With ActiveWorkbook.Names(strOldName)
.Name = strNewName
End With
Clearly, I'm doing something wrong assigning strNewName.
I have also tried using .text, .value, and trimming the string, all with the same non-result.
The non-working code does not produce an error. It just fails to change the name.
rngNamedRanges.Cells(1,6) refers to a cell containing straight text.
rngNamedRanges.Cells(1,8) refers to a cell containing a CONCATENATE formula which creates the new range name based on several other pieces of info contained in other columns.
Renaming is always a pain. Try the following:
Sub Rename()
StrOld = "MyRange1"
StrNew = StrOld & "_Renamed"
Range(StrOld).Name = StrNew
With ThisWorkbook
.Names(StrOld).Delete
End With
End Sub
Looping is up to you. :) Let us know if this helps.
Thanks for the input, all! I still don't understand why the first example I gave worked and the second one did not. Nonetheless, the following code appears to be working. I apologize for poor formatting of the snippet.
Dim rngNamedRanges As Range
Dim strOldName As String
Dim strNewName As String
Dim strRefersTo As String
Set rngNamedRanges = ActiveWorkbook.Worksheets("Named Ranges").Range("A2:K909")
i = 1
Do Until [CONDITION] = ""
strOldName = CStr(Trim(rngNamedRanges.Cells(i, 6).Value2))
strNewName = CStr(Trim(rngNamedRanges.Cells(i, 8).Value2))
strRefersTo = ActiveWorkbook.Names(strOldName).RefersTo
'Update all the formulas to use the new name.
For Each ws In Worksheets
If ws.Name <> "Named Ranges" Then
ws.Cells.Replace What:=strOldName, Replacement:=strNewName, LookAt _
:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End If
Next
'Delete old name and replace with the new one
ActiveWorkbook.Names(strOldName).Delete
ActiveWorkbook.Names.Add strNewName, strRefersTo
End If
strOldName = ""
strNewName = ""
i = i + 1
Loop
This is a really simple way to rename a range name. I got this from Paul Kelly at Excel Macro Mastery. Works great.
Sub rename_a_range_name()
Dim NewName As Variant
ThisWorkbook.Names("CurrentName").Name = "NewName"
End Sub
I modified the above code to rename some NAMES. With regards to the code immediately above, to loop through the worksheets and find/replace each NAME in formulas (etc)... I found that I needed to remove the Sheet Reference that is in the beginning of the string for each NAME's name.
'Update all the formulas to use the new name.
myStart = InStr(1, strOldName, "!", vbTextCompare) + 1
myLength = Len(strOldName) - myStart + 1
strOldNameSHORT = Mid(strOldName, myStart, myLength)
myStart = InStr(1, strNewName, "!", vbTextCompare) + 1
myLength = Len(strNewName) - myStart + 1
strNewNameSHORT = Mid(strNewName, myStart, myLength)
For Each ws In Worksheets
ws.Cells.Replace What:=strOldNameSHORT, Replacement:=strNewNameSHORT,
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False,
SearchFormat:=False, ReplaceFormat:=False
Next
Long story short
Putting the update into e.g. the worksheet change event is likely to work.
Root Cause
Update: UDFs called from cells are not allowed to change things on the sheet. (That's what I did)
Old: I guess there are certain calculation processing phases, where updates to names are allowed or not allowed.
More explanation and findings
I wanted to rename a range and it was sometimes ignored and sometimes I got an error 1004 (application- or object-defined error - in German: Anwendungs- oder objektdefinierter Fehler).
Let's say one has a rename function like this:
Function rename( nold As String, nnew As String ) As Boolean
ThisWorkbook.Names(nold).Name = nnew
rename = True
End Function
I found out the following:
if the update is triggered by some UDF (user-defined cell function) on some cell update it will be ignored in some cases and in other cases the 1004 error is raised
e.g. putting some =rename("oldName", "newName") into A1 where oldName exists
why and when it is ignored or the error is raised is unknown to me
if the update is triggered by some event, e.g. the Private Sub Worksheet_Change(ByVal Target As Range) it will always be applied
Other side-effects
In finding out all this and debugging it, it may have caused that cells got locked automagically and thus also causing some 1004 error.

Copy Range, Paste into New Workbook, Multiple Files, Delete Blank Rows

First time poster here. I've been reading tutorials/guides all day and made a lot of strides, but am having a tough time figuring out how to write a macro that does what I want to do.
I get around 100 time sheets per week that are then copied and imported into an accounting software. The sheets are all based off of a template, are in separate workbooks, and have a worksheet titled "Pre Import Time Card" within them. I copy the values from each book's pre import worksheet into a new file and upload them to our accounting software as a batch.
I want to have a macro open each file automatically, copy the range A1:I151 on each workbook, and then paste the values into a new worksheet. Because of the import templates design, this inevitably leads to many blank rows within the specified range. I would like to delete any blank rows as a final step.
UPDATE: I HAVE COPIED THE CODE TO REFLECT WHAT I CURRENTLY HAVE.Also a list of new problems is below.
pasting to next unused row is not working
I need to figure out how to kill the old file / not have it enter the same file twice.
I would like to suppress the "Privacy warning on VBA / Active X controls" dialog that comes up at each save.
It's not currently copying correctly. I'm getting a bug at the rDest.Resize line.
Object variable or With Block Variable not set.
I had it running when using file names in an array, but decided that was unnecessary and to use a For.. Each loop.
Sub CopySourceValuesToDestination()
Dim wbDest As Workbook
Dim wbSource As Workbook
Dim sDestPath As String
Dim sSourcePath As String
Dim aFile As String
Dim shDest As Worksheet
Dim rDest As Range
Dim i As Long
Dim TSSize As Object
Dim objFso As Object 'New FileSystemObject
Dim objFolder As Object 'Folder
Dim objFile As Object 'File
Set objFso = CreateObject("Scripting.FileSystemObject")
sDestPath = "Z:\Dropbox\My Documents\TimeSheets\Processed\"
sSourcePath = "Z:\Dropbox\My Documents\TimeSheets\Copying\"
'Open the destination workbook at put the destination sheet in a variable
Set wbDest = Workbooks.Open(sDestPath & "Destination.xlsm")
Set shDest = wbDest.Sheets(1)
Set objFolder = objFso.GetFolder(sSourcePath)
For Each objFile In objFolder.Files
aFile = objFile.Name
Set objWb = Workbooks.Open(sSourcePath & aFile)
'find the next cell in col A
Set rDest = shDest.Cells(xlLastRow + 1, 1)
'write the values from source into destination
TSSize = wbSource.Sheets(4).Range("A1").End(xlDown).Row
rDest.Resize(TSSize, 9).Value = wbSource.Sheets(4).Range("A1:I" & TSSize).Value
wbSource.Close False
wbDest.SaveAs sDestPath & "Destination.xlsm"
wbDest.Close
Kill sSourcePath & wbSource
Next
End Sub
Function xlLastRow(Optional WorksheetName As String) As Long
' find the last populated row in a worksheet
If WorksheetName = vbNullString Then
WorksheetName = ActiveSheet.Name
End If
With Worksheets(1)
xlLastRow = .Cells.Find("*", .Cells(1), xlFormulas, _
xlWhole, xlByRows, xlPrevious).Row
End With
End Function
Provded your datarange in the time sheet is continuous you can replace
rDest.Resize(151,9).Value = wbSource.Sheets(1).Range("A1:I151").Value
with
var for storing
dim TSsize as long
TSsize = wbSource.Sheets(1).Range("A1").end(xlDown).Row
rDest.Resize(TSsize,9).Value = wbSource.Sheets(1).Range("A1:I" & TSsize).Value
This is prevent the empty rows from getting into your sheet in the first place.
If the each time sheet is not a continuous range you can interate through the rows looking for empty rows and deleting them. Let me know if that is the case and i will update my answer.

Resources