Populating an excel file from word vba - vba

I'm writing a macro that will populate an excel file with user inputs from active x controls in word. I've got almost everything working except that I keep getting an error message when I try and select cell A1 in the sheet that I want to use in the workbook. Here is the code:
Workbooks.Open ("mypath\myfile.xlsm")
Workbooks("myfile.xlsm").Activate
Worksheets("sheet1").Select
Range("A1").Select
Do Until (IsEmpty(ActiveCell.Value))
ActiveCell.Offset(1, 0).Select
Loop
ActiveCell.Value = n
ActiveCell.Offset(0, 1).Value = a
ActiveCell.Offset(0, 2).Value = b
ActiveCell.Offset(0, 3).Value = c
Columns("D:D").EntireColumn.AutoFit
Columns("A:A").EntireColumn.AutoFit
Columns("B:B").EntireColumn.AutoFit
Columns("C:C").EntireColumn.AutoFit
Workbooks("myfile.xlsm").Save
Workbooks("myfile.xlsm").Close
The variables in this block of code are the values of the active x controls and are located much further up in the sub. This block of code is a small part of an if statement within the sub. Anyhow, when I take Range("A2").Select out of the code, it works just fine except for the fact that the information that I want to input does not go to the right spot (since it didn't select range A1 to begin with).
The error I get is type mismatch 4218.

Referencing the Excel object model gives you access to some global objects defined in that object model.
VBA resolves identifiers in this order:
Current procedure
Current module
Current project
VBA standard library
Host application object model
Any other referenced library, in the order they appear in the references dialog
So when you invoke Range meaning to be a call to the Excel object model, you actually invoke the same-name Range global member that's defined in the Word object model.
Note I say member and mean it: these are unqualified member calls to Global.Range. This is important, because a member implies an object, and since everything in the Excel object model (Word's too) has an Application property, then if you're not explicit about exactly what you're referring to, you might be implicitly creating an Excel.Application object, that you can't quite clean up properly. This usually translates into a "ghost" EXCEL.EXE process lingering in Task Manager well after your macro finishes running.
The trick is to make that reference explicit, and explicitly constrain its lifetime - a With block is perfect for this:
With New Excel.Application
With .Workbooks.Open(path)
With .Worksheets("Sheet1")
lRow = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
.Cells(lRow, 1) = n
.Cells(lRow, 2) = a
.Cells(lRow, 3) = b
.Cells(lRow, 4) = c
.Columns("A:D").EntireColumn.AutoFit
End With
.Save
.Close
End With
.Close
End With

I'm guessing as I don't usually run Excel from Word, but I think the problem might be related to everything being unqualified from Word.
If Workbooks.Open is working, then we can just hang everything related to that workbook on that..
Try the following code instead:
Dim myWkBk As Workbook, lRow As Long
Set myWkBk = Excel.Application.Workbooks.Open("mypath\myfile.xlsm")
With myWkBk.Sheets("sheet1")
lRow = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
.Cells(lRow, 1) = n
.Cells(lRow, 2) = a
.Cells(lRow, 3) = b
.Cells(lRow, 4) = c
.Columns("A:D").EntireColumn.AutoFit
End With
myWkBk.Save
myWkBk.Close

I've got it figured out. #Cindy Meister I just needed to add an ActiveSheet. qualifier on the troubled line:
Workbooks.Open ("H:\Second Rotation\OBI project\answersUsers.xlsm")
Workbooks("answersUsers.xlsm").Activate
Sheets("Answers Users").Select
ActiveSheet.Range("A1").Select
Do Until (IsEmpty(ActiveCell.Value))
ActiveCell.Offset(1, 0).Select
Loop
ActiveCell.Value = n
ActiveCell.Offset(0, 1).Value = cwid
ActiveCell.Offset(0, 2).Value = mann
ActiveCell.Offset(0, 3).Value = dept
Columns("A:D").EntireColumn.AutoFit
Workbooks("answersUsers.xlsm").Save
Workbooks("answersUsers.xlsm").Close
Dim myWkBk As Workbook, lRow As Long

Related

Error 1004 - Opening and activating workbooks

I have some issues while trying to copy/paste data between workbooks. I need to extract data from two different workbooks (A & B) to put it in a third one.
Since A & B have the exact same structure, I use the same code for both of them. However it works for A and I've got an error 1004 for B.
It seems that it happens when you do not specify the parent workbook/worksheet properly but I don't think this is the issue here since the code works for A.
If someone has an insight on this matter, I'm all ears!
Thank you for your help!
CH
Sub Data_Extraction()
Dim wb As Workbook, wba As Workbook, wbb As Workbook
Set wb = ActiveWorkbook
Set wba= Workbooks.Open("D:\xxx\A.xlsx")
Set wbb= Workbooks.Open("D:\xxx\B.xlsx")
Dim wsa As Worksheet, wsb As Worksheet
Set wsa = wb.Worksheets("a")
Set wsb = wb.Worksheets("b")
'I use a named variable here
X = Range("X")
If X=2 Then
''We fill the tab a''
For i = 9 To 400
wba.Activate
If wba.Worksheets("a").Cells(i, 2).Value = 5 Then
wba.Worksheets("a").Range(Cells(i, 1), Cells(i, 8)).Copy
wb.Activate
wsa.Range(Cells(7, 2), Cells(7, 9)).PasteSpecial Paste:=xlPasteValues
wsa.Range("B7").EntireRow.Insert
End If
Next i
''We fill the tab b''
For i = 9 To 400
wbb.Activate
If wbb.Worksheets("b").Cells(i, 2).Value = 5 Then
wbb.Worksheets("b").Range(Cells(i, 1), Cells(i, 8)).Copy
wb.Activate
wsb.Range(Cells(7, 2), Cells(7, 9)).PasteSpecial Paste:=xlPasteValues
wsb.Range("B7").EntireRow.Insert
End If
Next i
End If
End Sub
In lots of cases when you use Excel methods like protect/unprotect copy/paste, you should try to mimick as close as possible the configuration Excel would be in when a user would go through those steps, if you step away from that you are likely to wind up with instability cropping up in generic error codes as 5 and 1004.
In this case I believe you should do
wbb.Worksheets("b").Activate
before you start a copy from worksheet("b").
The real problem here is not that you didn't activate the sheet (though that does work, it's not a good solution)
wbb.Worksheets("b").Range(Cells(i, 1), Cells(i, 8)).Copy
Here the Cells() calls (unlike the Range() call) are not qualified by any worksheet object, so they will default to the ActiveSheet. In a regular module this is equivalent to writing:
wbb.Worksheets("b").Range(ActiveSheet.Cells(i, 1), ActiveSheet.Cells(i, 8)).Copy
...and is prone to failure when the active sheet is not what you expect.
This is robust and doesn't require you to activate a specific worksheet:
With wbb.Worksheets("b")
.Range(.Cells(i, 1), .Cells(i, 8)).Copy
End With

Unknown error in Excel VBA Macro code

I'm still quite new to VBA and I'm basically self-taught. I've developed a spreadsheet for work and I need a macro to allow customers to add information then the information copy to sheet 2 in descending order. This is the code I am using currently attempting to use but when I click on the “Save” macro button, the data stops copying over after two entries. Additionally, is there some code that I can input to clear the blocks so each new customer cannot see what the previous customer entered?
Private Sub CommandButton1_Click()
Dim Name As String, Org As String, POCPhone As String, Email As String, TypeofVeh As String, TotPax As String, TotCar As String, Pickup As String, DateReq As String, DateRet As String, Destination As String, YN As String, Remarks As String
Worksheets("TransReq").Select
Name = Range("B4")
Org = Range("C4")
POCPhone = Range("D4")
Email = Range("E4")
TypeofVeh = Range("F4")
TotPax = Range("G4")
TotCar = Range("H4")
Pickup = Range("I4")
DateReq = Range("J4")
DateRet = Range("K4")
Destination = Range("L4")
YN = Range("M4")
Remarks = Range("N4")
Worksheets("TransReqLog").Select
Worksheets("TransReqLog").Range("B3").Select
If Worksheets("TransReqLog").Range("B3").Offset(1, 1) <> "" Then
Worksheets("TransReqLog").Range("B3").End(xlDown).Select
End If
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = Name
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Org
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = POCPhone
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Email
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = TypeofVeh
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = TotPax
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = TotCar
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Pickup
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = DateReq
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = DateRet
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Destination
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = YN
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Remarks
Worksheets("TransReq").Select
Worksheets("TransReq").Range("B4").Select
End Sub
"the data stops copying over after two entries." - this means it stops here - ActiveCell.Value = POCPhone A possible reason should be, that POCPhone contains an error. E.g. - Range("D4") is probably #DIV/0 or #Value
There are 3 ways fix it (2 easy and 1 difficult) :
Write On Error Resume Next after Private Sub CommandButton1_Click() - this is really not advisable, because it will ignore every error. But it will fix it.
Rewrite the whole code, avoiding Select and ActiveCell (This is the difficult one). How to avoid using Select in Excel VBA
Write some check like this:
ActiveCell.Offset(0, 1).Select
If Not IsError(ActiveCell) Then ActiveCell.Value = DateRet
Here's a refactored version of your code that should do what you're looking for. Note that the code (including your original version) appears to assume that there is only one line (row 4) from your "TransReq" sheet to move over to the "TransReqLog" sheet:
Private Sub CommandButton1_Click()
Dim wb As Workbook
Dim wsData As Worksheet
Dim wsLog As Worksheet
Dim rData As Range
Set wb = ActiveWorkbook
Set wsData = wb.Sheets("TransReq")
Set wsLog = wb.Sheets("TransReqLog")
Set rData = wsData.Range("B4:N4")
wsLog.Cells(wsLog.Rows.Count, "B").End(xlUp).Offset(1).Resize(, rData.Columns.Count).Value = rData.Value
rData.ClearContents
End Sub
As a note, please familiarize yourself with How to avoid using Select in Excel VBA (Vityata also linked here in his answer)
Your code only works for two rows because of this line:
Worksheets("TransReqLog").Range("B3").End(xlDown).Select
The first line is copied successfully as the line of code isn't executed due to the IF statement.
The second line is successful as the code selects cell C3 and then performs the same operation as the keyboard shortcut Ctrl+Down which selects the next cell down that isn't empty. The code then offset by one row.
It breaks on the third attempt as the code does exactly the same as the second attempt - it starts at the empty C3 and moves down to the first cell that's not empty.
Providing all cells below are empty it's better to start at the bottom of the sheet and move upwards to the first cell that's not empty.
Worksheets("TransReqLog").Cells(Worksheets("TransReqLog").Rows.Count, 2).End(xlUp).Select
If there isn't a mixture of XL2003 and XL2007 or later then the you can just use Worksheets("TransReqLog").Cells(Rows.Count, 2).End(xlUp).Select
Having said all that, the refactor that #tigeravatar answered with is the way to go.

Excel macro - check for a string in a cell, run a routine, move to the next row, do it again

I am not a Dev, but given I do use Excel, I have been tasked to create a looping macro that will check for a string ('Resource') in a cell and if it finds that string, then run a Copy and Paste code and then move to the next row. This starts at row 5 and runs continuously until row 199, but does not work on every row, hence the validation for the string Resource.
I have managed to create the macro for the Copy and Paste but it also has issues as I created it using the macro recorder and it only works on the row I actually did the recording on.
I am at a complete loss, can anyone help?
this is what I have so far
A New Resource name is added manually to the spreadsheet
the user clicks cell (C6) to focus the curser
the user clicks a macro button called 'Forecast for Future Project 1' to start the macro
On the button click the Macro will:
Interogate if cell to the left of current cell (B6) = 'Resource'
IF Yes, THEN
Sub CP()
DO
Range("C6").Select
Selection.Copy
Application.Goto Reference:="ProjAdd"
ActiveSheet.Paste
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = _
"=SUMIF('Current Project Utilisation'!R2C1:R62C1,RC1,'Current Project Utilisation'!R2C:R62C)+SUMIF('Future Project 1'!R2C1:R62C1,RC1,'Future Project 1'!R2C:R62C)"
Range("ProjAdd").Select
Selection.Copy
Range("C6").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlToRight)).Select
ActiveSheet.Paste
Range("B6").Select
Loop Until ActiveCell.Address(0,0) = "$B$199"
End Sub
Move to cell under original active cell (C7) and Repeat the Macro until cell C199 is reached
If (B6) does not = 'Resource' then move to go to the cell under (C7) aand Repeat the Macro until cell C199 is reached
Refresh Worksheet to update data
Would something like this work for you?
Sub CopyPasteResource()
Dim CopyRange As Range
Dim Cell As Range
Set CopyRange = Workbooks("YourWorkBookName").Sheets("Sheet1").Range("C6:C199")
For Each Cell In CopyRange
If InStr(1, Cell.Offset(0, -1).Text, "Resource") Then
Cell.Copy
'paste where you wish
End If
Next Cell
End Sub
EDIT: Or do you want to loop through B6:B199 and then C6:199? I'm not entirely clear on the aim.
Ah the old macro recorder, generating 90% extra code since 1997. I couldn't exactly figure out from your question what exactly is being copied and to where but this code will loop through rows 5 to 199, check if the value in column B = "Resource" and then set the corresponding value in column C, you should be able to modify for your needs but I think you definitely want a structure more like this than what the recorder generated for you..
public sub cp()
Dim ws as Worksheet
Set ws = Worksheets("Current Project Utilisation")
Dim i as int
for iI = 5 to 199
if(ws.cells(i, 2).value = "Resource") then
ws.cells(i, 3).value = "what you're copying"
end if
next I
end sub
Assuming your cell range doesn't change you can do this for the looping part
Sub ResourceCheck()
Dim WS As Worksheet
Set WS = ActiveSheet
Dim Resources() As Long, r As Long
ReDim Resources(5 To 199)
For r = 5 To 199
If UCase(WS.Cells(r, 2).Value) = "RESOURCE" Then
WS.Cells(r, 3).Value = "x"
'Do copy paste part
End If
Next r
Application.Calculate
End Sub
Can you add a sample of your data? It's a bit hard to see what you're referencing to and how the data relates to each other.
Also, where is the "Projadd" cell reference? And what does it do?
Sub CP()
' I like to know what worksheet I'm on
Dim ws as Worksheet
' if it's a dedicated worksheet use this
' Set ws = ThisWorkbook.Worksheets("Sheet1")
' Otherwise following your current code
Set ws = ActiveSheet
' I also like to grab all my data at once
Dim Data as Variant
Data = ws.Range("B6:B199")
' No need to focus the cursor
For row = 5 to 199
' No need to select any range
' Is this case-sensitive???
If Data(row-4, 1) = "Resource" Then
' Copy C6??? Paste 'ProjAdd'
ws.Cells(row, 3).Copy Range("ProjAdd")
Application.CutCopyMode = False
End If
Next
End Sub

Duplicating Sheets in Excel

I'm trying to construct a spreadsheet to track project data, with a worksheet for each project and an overview sheet that provides summary information. I constructed a sheet based on a sample set of projects, and it all seems to work the way I expected. Adding a new project takes a lot of care and feeding, though, so I decided to give the user a button to click, a dialog to ask for a project name, and let the script do the rest.
I created a template sheet (conveniently titled "Template"), and I've attempted to script duplication of that sheet to the end of the workbook using the Worksheets("Name").Copy method as suggested on MSDN. This attempt is reflected in the first few lines of code below. After that I take a template row and add it to the end of the list on the overview sheet (named "Dashboard").
What ends up happening (as far as I can tell) is that the sheet doesn't copy, the last sheet in the workbook gets selected, and ActiveSheet.Name = Name ends up renaming it, blowing up my dashboard sheet that uses INDIRECT to find and represent data.
Here's the code I currently have (inefficient as it is):
Sub AddSOW()
' Duplicate the template sheet
Dim Name As String
Name = InputBox("SOW Name")
Worksheets("Template").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = Name
Sheets("Dashboard").Select
' Find the last row of data
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
' Loop through each row
For x = 2 To FinalRow
' Decide if to copy based on column A
ThisValue = Cells(x, 1).Value
If ThisValue = "Template" Then
Cells(x, 1).Resize(1, 50).Copy
NextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(NextRow, 1).Select
ActiveSheet.Paste
ActiveCell.Value = Name
End If
Next x
End Sub
No errors get thrown, it just doesn't work. Stepping through doesn't help either.
It turns out I was causing my own problem.
In order to ensure that the user doesn't modify the template worksheet, I had it hidden. Duplicating it was also duplicating the .Visible property setting, so the sheets were actually getting duplicated, but since they weren't visible, the last sheet in the book that was visible was getting selected, and therefore the one getting renamed.
I updated the code with some error checking so canceling the input box doesn't cause problems. Here's the working code.
Sub AddSOW()
Dim SName As String
SName = InputBox("SOW Name")
' Duplicate the template sheet
If SName <> "" Then
Worksheets("Template").Visible = True
Worksheets("Template").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = SName
Worksheets("Template").Visible = False
Sheets("Dashboard").Select
' Find the last row of data
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
' Loop through each row
For x = 2 To FinalRow
' Decide if to copy based on column A
ThisValue = Cells(x, 1).Value
If ThisValue = "Template" Then
Cells(x, 1).Resize(1, 50).Copy
NextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(NextRow, 1).Select
ActiveSheet.Paste
ActiveCell.Value = SName
End If
Next x
End If
End Sub

VBA Excel R1C1Formulas using cells method

I've run accross this problem many times and still haven't found the solution or why this won't work. I want to use cells method to enter a formula through a column and so I write this:(just an example)
With ws
iEndCol = .cells(4650,1).End(Xlup).Column
For i = 2 To iEndCol
.Cells(i, 2) = "=VLOOKUP([RC-1],Somesheet!someTable,10,FALSE)"
Next
End With
when this dosen't work (Method error) I try something like this:
Cells(i,2).Select
Do While IsEmpty(ActiveCell.Offset(0, -1)) = False
ActiveCell.Formula = "=VLOOKUP([RC-1],Somesheet!someTable,10,FALSE))"
ActiveCell.Offset(1, 0).Select
Loop
or instead of .Formula, I try .FormulaR1C1, .Formulalocal etc. and this doesn't work either. Then this is what works:
Range("B2").Select
Do Until IsEmpty(ActiveCell.Offset(0, 5)) And IsEmpty(ActiveCell.Offset(0, 6))
If IsEmpty(ActiveCell) = False Then
ActiveCell.Offset(0, 1).Formula = "=VLOOKUP(B2,Somesheet!someTable,10,FALSE)"
End If
ActiveCell.Offset(1, 0).Select
Loop
What am I not understanding on using Cells to enter formulas?
Enter a formula using Excel interface (not your code).
Now go to the code editor, press Ctrl+G and type: ? activecell.FormulaR1C1
The result, =VLOOKUP(RC[-1],Somesheet!sometable,10,FALSE), will tell you what you are doing wrong. You are not providing correct RC syntax.
Having that said, you should always ensure your formula syntax matches the property you have picked to set that formula. Use A1 notation for .Formula, and RC notation for FormulaR1C1. And don't use .Value to set a formula.
First, the following worked for me:
Set oCell = ActiveCell
Do
Set oCell = oCell.Offset(0, 1)
oCell.FormulaR1C1 = "=VLOOKUP(RC[-1],SomeTable,10,FALSE)"
Set oCell = oCell.Offset(1, -1)
Loop Until IsEmpty(oCell)
Notice that in my syntax, I assumed that SomeTable was a defined name with Workbook scope and thus I need no prefix. If SomeTable is a defined name scoped to a specific Worksheet, only then do you need to prefix the sheet name (e.g. Somesheet!SomeTable).
Second, you should verify in which cell it is trying to put the formula using Debug.Print oCell.Address. It may be the case that it is trying to stuff the formula in literally the first column which would cause an error in the formula.