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.
Related
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
So I am trying to build code where if this string of characters is present then I want to copy the entire row of the active cell and paste it on to a new workbook.
This is what the column I am trying to filter sort of looks like:
cells with 0 - multiple entries
I keep getting an object-defined error that I am unsure how to resolve. Here is the code I am working with
Sub moveData(ByVal i As Long, ByVal j As Long, ByVal k As Long, ByVal row_sum As Double)
Worksheets("HHP").Range("BN").Select
' Change A2 to whatever cell you want the data to be copied to
Worksheets("FastTrackTool").Range("BN").Select
For i = 16 To row_sum
Do Until IsEmpty(ActiveCell)
If ActiveCell = "D0Y" Or "D0Z" Or "D1B" Or "D1C" Or "D1D" Or "D0M" Then
MsgBox (ActiveCell)
ActiveCell.EntireRow.Copy
Worksheets("HHP").Select
With Selection
.PasteSpecial Paste:=xlPasteValues
End With
ActiveCell.Offset(1, 0).Select
Worksheets("FastTrackTool").Select
End If
ActiveCell.Offset(1, 0).Select
Loop
Application.CutCopyMode = False
Next
End Sub
There are two immediate issues I can see with your code. The first problem is your IF statement.
If ActiveCell = "D0Y" Or "D0Z" Or "D1B" Or "D1C" Or "D1D" Or "D0M" Then
The OR doesn't work like this - you need a condition to check between each OR, and it needs to evaluate to True or False. I fixed this in the code below, though there is an even better, much more concise way to do it with a Select Case statement
If ActiveCell.Value = "D0Y" Or ActiveCell.Value = "D0Z" Or ActiveCell.Value = "D1B" Or ActiveCell.Value = "D1C" Or ActiveCell.Value = "D1D" Or ActiveCell.Value = "D0M" Then
The other problem is that you are trying to compare the Range object as Jacob H mentioned. If you would like to compare that cell's value, then use the .Value attribute/member (terminology?) to access this. There are two instances of this mistake, which I also fixed in the below code.
NOTE: One instance might not actually be a mistake - I changed the MsgBox(ActiveCell) to MsgBox(ActiveCell.Value). However, you may have intended it to pop up with the address (ActiveCell.Address), or some other attribute.
Your code, with my noted revisions:
Sub moveData(ByVal i As Long, ByVal j As Long, ByVal k As Long, ByVal row_sum As Double)
Worksheets("HHP").Range("BN").Select
' Change A2 to whatever cell you want the data to be copied to
Worksheets("FastTrackTool").Range("BN").Select
For i = 16 To row_sum
Do Until IsEmpty(ActiveCell)
If ActiveCell.Value = "D0Y" Or ActiveCell.Value = "D0Z" Or ActiveCell.Value = "D1B" Or ActiveCell.Value = "D1C" Or ActiveCell.Value = "D1D" Or ActiveCell.Value = "D0M" Then
MsgBox (ActiveCell.Value)
ActiveCell.EntireRow.Copy
Worksheets("HHP").Select
With Selection
.PasteSpecial Paste:=xlPasteValues
End With
ActiveCell.Offset(1, 0).Select
Worksheets("FastTrackTool").Select
End If
ActiveCell.Offset(1, 0).Select
Loop
Application.CutCopyMode = False
Next
End Sub
I haven't tested this code, but those are the issues that pop out to me. Let me know if this fixes your error.
Cheers and good luck!
I'm currently having a list of keywords (e.g. CFO, CTO, interim manager, etc.) and I want to have a macro assigned to a button that can search all cells in Column E of Sheet 1 which contain these keywords then give back the result as well as highlight the keyword in the cell.
Each keyword is in a separate cell in Column A of Sheet 2.
If there is one keyword in the list, it will search for one but if there are more, it will search for combination.
Here is the screenshot to illustrate what I've describe above
I have found something over the Internet with suggestion to use AutoFilter but I can only use it to perform a search for one keyword. This is what I've tried:
Sub EmailFilter()
Application.ScreenUpdating = False
With Worksheets("Sheet1").Columns("E:E")
.AutoFilter Field:=1, Criteria1:= _
"=*" & Worksheets("Sheet2").Range("A2:A10") & "*", Operator:=xlAnd
End With
Application.ScreenUpdating = True
End Sub
Thanks in advance.
The below code will color all the matches with the same color(I have chosen blue). You can write this macro in a module and then create a Form Control Button and assign the macro to the button.
Sub macro()
Dim a As Integer, x As String, mystring As String
a = 2
Sheets("Sheet2").Activate
Cells(a, 1).Activate
Do While ActiveCell.Value <> ""
x = ActiveCell.Value
p = Len(x)
Application.GoTo Sheet1.Range("E2")
Do While ActiveCell.Value <> ""
mystring = ActiveCell.Value
If InStr(mystring, x) > 0 Then
Position = InStr(1, mystring, x)
If Position > 0 Then
ActiveCell.Characters(Position, p).Font.Color = RGB(255, 0, 0)
End If
End If
ActiveCell.Offset(1, 0).Activate
Loop
a = a + 1
Application.GoTo Sheet2.Cells(a, 1)
Loop
End Sub
Let me know if you have any other specific requirements so that the code can be altered. I hope this helps.
I need to locate a date entered by a user in a specific column. If the date is found, the macro should check the third field to the right:
if it's blank, it should copy and paste a specific value from another sheet;
if it's not blank, just pop out a message box.
The current code does not do a copy-paste and somehow it is not running through a list of dates though it has been already working before.
Sub EnterRecord()
Dim rcdDate As Date
Dim r As Range
Set ws1 = Sheets("Manual")
Set ws2 = Sheets("Data")
ws1.Activate
rcdDate = InputBox("Please enter date dd/mm/yyyy")
With ws1.Range("K:K")
Set r = .Cells.Find(What:=rcdDate)
If Not r Is Nothing Then
r.Select
ActiveCell.Offset(0, 3).Activate
If ActiveCell.Value = "" Then
ws2.Range("b1").Copy
ws1.ActiveCell.Value.Paste Paste:=xlPasteValues
End If
MsgBox "Date is incorrect or the record is already entered"
End If
End With
End Sub
If you want to search for the date in column K of ws1 and you already are using the With then replace:
Set r = Cells.Find(What:=rcdDate)
with
Set r = .Cells.Find(What:=rcdDate)
There may be other problems.
EDIT#1
Once you have executed:
ActiveCell.Offset(0, 3).Activate
you have "moved" the ActiveCell, so replace:
If ActiveCell.Offset(0, 3).Value = "" Then
with
If ActiveCell.Value = "" Then
I'm writing a Macro which loops though the Excel data, which is sorted by column A and inserts a blank row if the the values for coulmn are different from the one above. This separates my data in groups by column A.
I then want to sum the value of column d of the separated groups. I have most of my code working underneath, however its the startCell variable I am having trouble with. I know what I want to do, but cant get the logic right, can someone please help sum up those individual groups.
Many thanks
Sub PutARowInWithFormula()
Range("A3").Select
Dim startCell As Integer
Dim endCell As Integer
startCell = 3
endCell = 0
Do Until ActiveCell.Value = ""
If ActiveCell.Value = ActiveCell.Offset(-1, 0).Value Then
ActiveCell.Offset(1, 0).Select
Else
' I need the bottom code to execute only once in the loop
' startCell = ActiveCell.Row
ActiveCell.EntireRow.Insert
' move to column d
ActiveCell.Offset(0, 3).Select
endCell = ActiveCell.Row - 1
ActiveCell.Formula = "=Sum(d" & startCell & ":d" & endCell & ")"
' move back to column a
ActiveCell.Offset(0, -3).Select
'move 2 rows down
ActiveCell.Offset(3, 0).Select
End If
Loop
End Sub
I am too wondering, why you don't use a PivotTable or just create this using worksheet functions, which is possible too. Also I do not really like this attempt with selections, but its your way, and I respect that. It even seems to be a quite good example of a situation, when it might be a good idea to use them. Because right now, any other way I could think of, to do this in VBA, seems to be more complicated.
So here is a fix up of your code:
Sub PutARowInWithFormula()
Range("A2").Select
Do Until ActiveCell.Value = ""
If ActiveCell.Value = ActiveCell.Offset(-1, 0).Value Then
ActiveCell.Offset(1, 0).Select
Else
ActiveCell.EntireRow.Insert
'you can use the offset directly
'by using an improved formula, you do not need to know start and end row.
ActiveCell.Offset(0, 3).Formula = _
"=SUMIF(A:A,OFFSET(INDIRECT(""A""&ROW()),-1,0),D:D)"
' move back to column a and move 2 rows down
ActiveCell.Offset(2, 0).Select
End If
Loop
End Sub
Edit
Ok, found a way easier way to do nearly the same thing:
Public Sub demo()
UsedRange.Subtotal GroupBy:=1, Function:=xlSum, TotalList:=4
End Sub
This function is also available through the ribbon-menu -> data -> sumsum
To avoid the error-message, you just need to have a title-row for your data, like:
DATE | NAME | COUNTER | VALUE