I have a series of macros that will insert data pertaining to which button is selected. The problem that I am having is that I have a series of 5 buttons each running a macro containing a different number of rows of data. After the data is entered from the macro, I have the same 5 buttons for the user to select another group of data. Since each macro is a different number of rows it keeps screwing up the formating (example: the 2nd set of macros is set to run in row 3, but when the first set is selected the data continues until row 5). Any advise on how to fix this?
Here is my macro's code:
Sub Realestate()
Sheet14.Unprotect Password:="Loan101"
Range("AV1:CP20").Select
Selection.Copy
ActiveWindow.ScrollColumn = 4
ActiveWindow.ScrollColumn = 3
ActiveWindow.ScrollColumn = 2
ActiveWindow.ScrollColumn = 1
Range("A11:AU11").Select
Selection.Insert Shift:=xlDown
Range("U13:AA13").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = "=Application!R[10]C[3]"
Range("I14:Q14").Select
ActiveCell.FormulaR1C1 = "=Application!R[119]C[9]"
Range("K24:O24").Select
ActiveCell.FormulaR1C1 = "='Consumer Loan Request'!R[-4]C[-1]"
Range("P24").Select
Rows("20:20").RowHeight = 3
Rows("30:30").RowHeight = 3
Range("j12:w12").Select
Sheet14.Protect Password:="Loan101"
End Sub
The code needs several fixes to be optimized, but the way SO works is to answer one question each time so that future users will find it useful as well. I will hence, in this thread, limit to answer your question: "How can I make a macro starting from the row where the previous one ended?"
The trick is to store the value of the row in a global variable, i.e. a variable whose scope is not limited to the single macro but to the whole module. In order to reach this (please adapt this small example to your code, since nobody can see the project so this can only be a hint):
1) Declare the global variable on top of all macros:
Dim rowStart As Long
2) Write your macros:
Dim rowStart As Long
Sub Macro1()
'code
End Sub
Sub Macro2()
'code
End Sub
So, here's the hint. Make your first macro perform some operations, for example putting 20 random numbers into the first 20 cells of the column "B":
Sub Macro1()
For j = rowStart To rowStart + 20 'rowStart has not been modified yet, so it's equal to 0 by default
Sheets("YourSheet").Range("B" & j+1).Value = Rnd() '"B" & j will be "B1" the first time, "B2" the second time etc.
Next j
rowStart = j 'here we store the value "20", i.e. the last value of j, into the global variable so that we can reuse it after
End Sub
Once this is done, let's say that your second macro will want to add other 10 random numbers to the existing list already created from the first macro. To do this:
Sub Macro2()
For j = rowStart To rowStart + 10 'now rowStart = 20, so we will start from the last row where the previous macro ended!
Sheets("YourSheet").Range("B" & j+1).Value = Rnd()
Next j
rowStart = j 'now we store the value "30", so next time you want to know the last row used, you will know it
End Sub
Unfortunately, your code is very complicated to read and understand what it's doing (I imagine it's recorded since I still see the ActiveWindow.ScrollColumn command coming from you moving up and down your spreadsheet manually).
So, my suggestion is to start from these two tips:
Global variables will help you to keep in memory the last rows touched from your macros;
String merging (Range("B" & j)) will help you to reference objects dinamically.
In general, use the Macro recorder to learn about the commands, but write your own code: it might take a bit longer in the beginning, but you will make sure that the code is flexible and able to adapt to any kind of similar problem. Here's a good tutorial to get started with.
Related
I am new and I am working in a VBA - Excel Project.
My project has some Sub-projects, lets call them A,B,C...
Through VBA code and some actions I create new sheets that are called A-item-1, A-item-2 or B-item-1 or with C.
The thing is that I am creating these sheets by copying a template and printing it before the next letter.
For example, B-item-3 is created copying my B-item-template and printing it before C sheet.
Now, the way the program works you can create B-item-1, B-item-4, hit the button and it will create them.
If after that you want to create B-item-2, as the program puts it before C you will have this:
A,B,B-item-1,B-item-4,B-item-2,C (this is my workbook)
And I am thinking on a code to rearrange only "B-item-X" sheets.
As far as i know i should put these sheets in an Array.
Then i should, somehow, get the number from every sheet to a variable.
And then compare with a for these sheets and if the number is less than the sheet i am comparing it to, move that sheet.
I think it could look like this?
1st - Determine the Range of sheets I want to rearrange
2nd - Somehow extract the number of each "B-item-X" sheet
3rd-->
For i=1 to Range of sheets -1
For j= i + 1 to Range of sheets
if The number on the name of the (j) Sheet is < The number on the name of the (i) Sheet then
Sheets(j).Move before:= Sheets(i)
End if
Next j
Next i
I hope it is easy to understand what i want to do. If not, hit me up and i will try to explaint it with more details. I hope someone can help me. Thank you very much.
Edit:
The way the program works. The user writes an input and based on that I have a non visible chart in which i write 0 or 1 depending on the input of the user. Then the program creates the ITEM sheet if it sees a 1 on the chart.
As an example, for the 20 items i can create:
The user puts YES (1 in the Chart) on ITEMS 1,7 and 15 and presses the button OK.
The program creates them and you would have these sheets:
A,B,B-item-1,B-item-7,B-item-15,C,D...
But the program is still used for next Batches, lets say.
So the next day the user puts YES on the item 9. The program will create the "B-item-9" sheet before C sheet but it will be put after the "B-item-15" sheet because it was already crated the day before that.
The thing is i do not know how to move them to the right place when creating them nor do i know how to rearrange them...
Try this
Sub SortSheetsTabName()
Dim scrUpdating As Boolean: scrUpdating = Application.ScreenUpdating
Application.ScreenUpdating = False
Dim i As Long, j As Long
Const txtBefNum As String = "B-item-"
With ThisWorkbook
For i = 1 To .Sheets.Count - 1
If .Sheets(i).Name Like txtBefNum & "*" And _
IsNumeric(Mid(.Sheets(i).Name, Len(txtBefNum) + 1)) Then
For j = i + 1 To .Sheets.Count
If .Sheets(j).Name Like txtBefNum & "*" And _
IsNumeric(Mid(.Sheets(j).Name, Len(txtBefNum) + 1)) Then
If CLng(Mid(.Sheets(i).Name, Len(txtBefNum) + 1)) > _
CLng(Mid(.Sheets(j).Name, Len(txtBefNum) + 1)) Then
.Sheets(j).Move before:=.Sheets(i)
End If
End If
Next j
End If
Next i
End With
Application.ScreenUpdating = scrUpdating
End Sub
This will sort all worksheets that have a name starting with 'B-item-' according to the number in their name following that text from smallest to largest.
This question already has answers here:
Excel VBA, getting range from an inactive sheet
(3 answers)
Closed 1 year ago.
I am a newbie to the VBA world and need your help.
I want to copy data from columns A, B and C in Sheet2 and Sheet3 and paste it in Columns A, B and C of Sheet1 but stacked. Meaning, data from Sheet2 should be pasted in "A1:A4", then data from Sheet3 should be pasted in "A5:A9".
I am using the following code and getting an error:
j = 1
For i = 1 to 2
For k = 1 to 3
Sheets(i+1).range(cells(1,k),cells(4,k).copy
Sheet(1).range(cells(j,k),cells(j+3,k).PasteSpecial xlPasteValues
Next k
j = j + 4
next i
If there is a better way to do it, it would be helpful too.
Please help!
The difference between your code and the one submitted below is in the effort expanded on preparation. Preparation led to recognition of problems where lack of preparation led to confusion and, ultimately, this question. What I'm saying is that I don't think you would have needed to ask had you known to prepare adequately.
Private Sub Snippet()
' 218
Dim Ws(1 To 2) As Worksheet ' "Source 1" and "Source 2"
Dim i As Integer ' loop counter: Ws index
Dim Rs As Long ' loop counter: rows (source)
Dim WsOut As Worksheet ' "Output"
Dim Rt As Long ' target row
With ThisWorkbook
Set WsOut = .Worksheets("Output")
Set Ws(1) = .Worksheets("Source 1")
Set Ws(2) = .Worksheets("Source 2")
End With
With WsOut ' = last used row in WsOut
Rt = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Application.ScreenUpdating = False
For i = 1 To 2 ' loop through Ws(1 to 2)
With Ws(i) ' find last used row in Ws(1)
' start copying from row 2 because row 1 probably holds captions
' end with last used row in column A
For Rs = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
Rt = Rt + 1
.Range(.Cells(Rs, 1), .Cells(Rs, 3)).Copy
WsOut.Cells(Rt, 1).PasteSpecial xlPasteValues
Next Rs
End With
Next i
With Application
.CutCopyMode = False
.ScreenUpdating = True
End With
End Sub
The procedure starts off by naming all variables. The first result was to replace references like "Sheet(1)" even at this early stage because "Sheet(1)" is the first sheet from the left in the tab bar. If you add a sheet unwittingly your code won't work anymore or, rather, it will destroy your workbook. Your variables "k" and "j" were replaced with "Rs" and "Rt" marking them as row numbers (source and target).
Next, the code makes sure that the worksheets are properly defined as being within ThisWorkbook. They are also properly linked to their real life names, executing a decision made at the beginning. Actually, the variable declarations are modified many times while the rest of the code is developed. It isn't hewn in stone at the beginning but everything is built on it nevertheless.
Then the Target Row is set, and a system for it's maintenance designed. The system is to find the last used row first and then increment that number every time before a new row is written.
The decision is made to turn off ScreenUpdating while the code runs. It will run faster that way but you must make provision to turn the feature back on at the end. That part of the code is written at this time.
And only now I arrive at the point which you had started out with. My code is remarkably like yours. Note that Copy/PasteSpecial allows you to choose what to paste, in this case "Values". You might use Copy with Destination instead which would also include formats. Or you might specify to copy formulas instead of values. To copy values you could simply use syntax like WsOut.Cells(Rt, 1).Value = .Cells(Rs, 1).Value` in a little loop. Because of the solid fundament on which this code is built it's very easy to modify these two lines using the established components.
The code ends on setting Application.CutCopyMode = False and presenting the result of the action in the display by restoring ScreenUpdating.
I am having trouble creating some vba for a project that involves real time intervals.
The sheet originally created and used for this some 8 years a ago has disappeared in a Windows update last year. For the life of me I can not recreate the code after all this time.
I want to have have the data in column A move to the next column ater a specific time interval. Column A has real time data that changes in milliseconds and I want to record data from that column at a set time interval eg 1 minute.
At the next 1 minute mark, the new data in column A is to be moved into column B and the previous saved data in Column B is now pasted into Column C. At the next 1 minute mark the data in Column A goes to B, B goes to C and C goes to D etc.
Attached is a mockup of how it would look in the sheet, with original data feed in Column A and the captured data from the feed every 1 minute moving across the sheet -
Data Mockup
What I did find was a snippet of code I started with that may explain what I was trying to achieve. The friend that helped me with this in 2012 is no longer alive but he started with this -
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Columns.Count <> 16 Then Exit Sub
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Range("b5:b30").Value = Range("b5:b30").Value
Range("b5:b30").Value = Range("a5:a30").Value
I tried to copy/paste and change the `Range' lines in the VBA page for each column but it didn't work. I am actually hoping that there may be a simpler way to do this rather than writing the range lines for each column of the sheet.
Any help is appreciated
Try this one:
Dim i As Long
Sub test()
If i = 0 Then
i = 1
Else
i = i + 1
End If
Range("A5:A34").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B3").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B3").Value = "Minute " & i
Application.OnTime Now() + TimeValue("00:01:00"), "test"
End Sub
Sub Reset()
i=0
End Sub
First line is to declare one counter that will work each time that you use the code. One Procedure call test that will shift the values one the first column to the second one, as well as it will introduce the header. After one minute, it will shift 1st and 2nd columns and so on. There is another procedure to reset the counter.
In order to make it easy for your, just create 2 buttons on top of the header like "Run" and "Reset" and call this macros from there.
If you have any issue, just let me know.
Hope it helps!
I am a newbie to VBA but got tasked to work with it anyway. So my task is to build a macro that takes data from different sheets and puts it below each other in one result sheet ("Tabelle1" in my example). The input data in each sheet is stored in blocks of two columns, right next to each other - so columns A and B have to import into the result sheet, then C and D and so on. Doing this for one sheet is not a problem:
Sub Makro1()
'
' Makro1 Makro
'
Dim Erste As Long
Dim k As Long
Dim j As Long
k = 1
j = 2
Do
Sheets("Tabelle1").Select
Erste = ActiveSheet.Cells(Rows.Count, 3).End(xlUp).Offset(1, 0).Row
Sheets("Tabelle2").Select
Range(Cells(5, k), Cells(5, j)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Tabelle1").Select
Cells(Erste, 3).Select
ActiveSheet.Paste
k = k + 2
j = j + 2
Loop Until Sheets("Tabelle2").Cells(4, k).Value = ""
End Sub
But I not only have one input sheet ("Tabelle2" in this example) but several (up until sheet20 or so). And all of them are built the exact same way, only with different data in each. What I would need the macro to do is, when reaching the empty cell in the first input sheet ("Tabelle2), go to the next input sheet ("Tabelle3") and continue the import of the data.
It doesn't sound too hard to do at first, but I cannot seem to find a solution. If anyone could help me out, it would be very much appreciated :-)
I know that the macro itself is very badly written and I can get rid of most of the Select. But as long as it works I'm fine.
Instead of using the name of the sheet, use the Index
Example: Sheet(1) and Sheet(2) etc
You can use that number as a variable that you can increment.
Example:
Dim i as Integer
Sheet(i).Select
Note:
It is also better practice to change the code to not rely on .Select as it can cause confusion and problems.
In addition, it would be better to use Worksheet(1) as charts can also be referred to as sheets.
After trying a few different approaches I am stuck. Basically I have a table where a line is completed every 30 mins or so. The User will enter 3 numbers in a row, the next 3 rows are calculated. I would like to use one of the calculated rows as a trigger to copy the entire row to a DB sheet (sheet2). And repeat when the next line is added and so on. I can only seem to get the 1st line to copy across every time.
The sheet is used as a hardcopy batch record so I have shied away from building a FORM. I started with the following, which worked for line 1 (also calling a recorded macro to copy in some header data)
`Private Sub Worksheet_Calculate()
If IsNumeric(Range("$h$9")) Then
If Range("$h$9").Value >= 1 Then
Application.Run "Macro1"
End If
End If
End Sub `
Macro1 just copying some of the header form data (date/time,machine info etc that is only entered once for the sheet & the row described above).
I hope this is clear, any questions please let me know
Welcome to SO
Try using a loop like below? Change the upper limit of i as you want, and maybe use some form of row counter like Cells(Rows.Count, 1).End(xlUp).Row
Private Sub Worksheet_Calculate()
Dim i As Integer
For i = 1 To 9000
If IsNumeric(Cells(i, 8)) Then
If Cells(i, 8).Value >= 1 Then
Application.Run "Macro1"
End If
End If
Next i
End Sub