Uh hello stackoverflow, I just started learning excel VBA for works and I find a problem that is I have to manually change the reference cell of a button manually for 500 buttons I wanted to make. For simplification sake:
Say in Column A I have several information normally hidden.
I put a button in each cell of Column B which will display the information in Column Ax (x being the cell number) to Cell Cx. Say:
Range("C1").Value = Range("A1").Value
The button I put in B1 would do the above, that is showing the value of C1 to be what is put in A1.
Now, I have 500 rows of data like this. Is it possible to make a dynamic buttons that knows the cell it is positioned in, and when moved/copy pasted to another cell, refer to its new location to dynamically change the reference cell in its code (Ax and Cx in that example)? So if I put the button in cel B67, the reference cell in the button code would change to C67 and A67 respectively.
[Before answering, here's my take on the things:]
I think the question you're trying to ask here is "how to find the last non-empty row".
The reason why I'm not flagging it as one is because, you may not be aware that you actually should be asking that question.
In general (and without meaning to sound condescending) you probably should study up on the programming basics first before asking questions like this here. Take some basic course somewhere (there are many) and only then begin experiment with some coding, because if you're missing coding fundamentals, then you'll never understand the bigger pictue behind it
What you're trying to describe here is a basic loop in programming language.
How does the loop work?
Let's pretend that we're a computer and we could also speak (in human language, not binary 1s and 0es).
I want to go through all the cells in a dynamic range
I need to know where does the dynamic range start and end (fetch me ending point)
I'll begin at starting point and end at ending point (duhh, silly humans should know that...)
For each and every cell in the range I'll do a certain action.
And that's it, fairly easy. Was a matter of seconds.
If we were trying to exactly represent what I described here:
Option Explicit ' < prevents you from typos, undeclared variables and so on...
Private Sub eachActiveCell()
Dim actionrange as Range
Dim lastrow as Long
lastrow = Sheets("Your Sheet Name").Cells(Rows.Count, "A").End(xlUp).Row
'finds last (active) row ^
Set actionrange = Sheets("Your Sheet Name").Range(Cells(1, "A"), Cells(lastrow, "A"))
' this sets our range to (A1:A <lastrow>) ^
Dim cell as Range
For each cell in actionrange
cell.Offset(0, 2) = cell.Value2 ' utilizes offset, A1 offset by 2 columns = C1
Next cell
End Sub
Other alternative, as it may be easier to comprehend with rows, rather than Ranges.
Option Explicit
Private Sub foriinA()
Dim ws as WorkSheet: Set ws = Sheets("Your Sheet Name")
Dim lastrow As Long
lastrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim i As Long
For i = 1 To lastrow
ws.Cells(i, "C") = ws.Cells(i, "A")
Next i
End Sub
Related
I have no experience with VBA and would love some help. As the title indicates, I'm looking for a script that fills a certain number of blank cells within column G with the average of all nonblank cells within that same range (e.g. fill all blank cells in G16:G59 with the average of all nonblank cells within G16:G59).
To make things more complicated, I'd need to vary the range somewhat dynamically as I wouldn't be sure as to how many rows I'd need to apply this script to and a work colleague who would be using this script might not have any experience with VBA either... The easiest solution I can think of is to have another cell contain the name of the last row in the range, or something like this: "Fill all blank cells in G16:Gx with the average of all nonblank cells within G16:Gx, where x = the row name listed in cell G12". Within G12 I'd have a text that states the last row to define the range, e.g. cell G12 contains the text "G80", which makes the range within the script to read G16:G80.
I know I'm asking for a lot, so if you can even just provide guidance on the first bit, I'd be very grateful! Thank you in advance for your time.
I think we can all remember what it was like when we first started out with VBA coding – which is why I’m helping you here. Normally, you’d be lucky to get any assistance with your question without providing at least some code & a description where it wasn’t doing what you wanted. Using the Record Macro button is always a good place to start.
Assumptions made here are that the data is on “Sheet1” in your file, and that there is a value in the last cell in Column G in the range you’re interested in. If that isn’t the case, let me know and I’ll show an alternative method to find the last row.
I’ve added descriptions about what (most) code does in each case to help you understand what’s going on. Let me know how you go with it.
Option Explicit '<~~ get in the habit of putting this at the top of your code
Sub FillInBlanks()
Dim ws As Worksheet '<~~ declare all variables
Dim LastRow As Long '<~~ use Long not Integer
Dim cel As Range '<~~ use intuitive variable names
Dim avg As Double '<~~ Double used here if you want decimal places in the average
Set ws = Sheets("Sheet1") '<~~ be explicit with sheet references
'Best practice to determine last used row in column
LastRow = ws.Cells(Rows.Count, "G").End(xlUp).Row '<~~ finds the last used row in column G
'Calculate the average & assign it to the variable "avg"
avg = Application.Average(ws.Range("G16:G" & LastRow))
'Loop through each cell in the defined range - one at a time
For Each cel In ws.Range("G16:G" & LastRow)
If cel = "" Then '<~~ = If the cell is blank, then...
cel = avg '<~~ ...put the average value (avg) in the cell
cel.Font.Color = RGB(51, 102, 0) '<~~ change color to suit
End If
Next cel '<~~ go to the next cell in the range
End Sub
I am attempting to select a dynamic range of filtered data that spans from col. A: col. J without selecting the header (in row 1). From there I need to copy and paste it into a new sheet where I will manipulate it further, but I cannot come up with an efficient or functional way to do this. Based on some code I found on another forum I was able to select all of the "visable cells" in a single column, but I am running into issues trying to select the whole range. I am still very new to vba so forgive my syntax, but my code posted below was an attempt to itterate through Rows.Count and i which was an integer 1-10. If you have any advice on how to do this better and more efficiently I would really appreciate it.
Sub SelectVisibleInColD()
Dim lRow As Long, i As Integer
Set i = 1
Do While i <= 10
With ActiveSheet
lRow = .Cells(.Rows.Count, i).End(xlUp).Row
If lRow < 3 Then Exit Sub
.Cells(1, 1).Offset(1, 0).Resize(lRow - 1).SpecialCells(xlCellTypeVisible).Select
End With
i = i + 1
Loop
End Sub
You can select a range by using Range property of ActiveSheet. You already have the last row and you know that the header is in the first row, so your range starts from position A2 and goes to the last row of column J
ActiveSheet.Range("A2:J"&lRow).SpecialCells(xlCellTypeVisible)
If you want to copy this range, use Copy function like
yourRangeAsAbove.Copy
This function only moves the selection to memory, to paste it, build your destination range and call PasteSpecial function.
I came across this answer googling my issue for: deleting of filtered selection in vba.
However trying your answer &lRow gives me an runtime error 1004, application-defineed or object-defined error
I got around it with this
ActiveSheet.Range("A2:G" & Range("A" & Rows.Count).End(xlUp).Row).SpecialCells(xlCellTypeVisible).Delete
For those that may also get the same issue.
If was calling into a radio show I'd say, "Long time listener...first time caller". I have learned so much from this site for a long time, but have never had a scenario I could not find an answer to...until today.
I need to perform multiple steps within a loop and my mind just gets twisted in knots trying to figure out how to do it because I need to pull stuff from different columns in different sheets within the same loop. I hope the brilliant minds here can bring some order to the chaos!
Note: I know how to do most of these things individually in a vacuum. It's all of it together...with all of the conditions...that has me flummoxed such that I just don't know where to start.
What I need to do is:
Look in a worksheet named "Data"
Create a new worksheet for each value in column B of that "Data" worksheet (not including row 1/headers) and name the worksheet the same thing.
THEN on the worksheet we just created, copy/paste all data (complete range w/headers) from another existing worksheet WHERE the worksheet name = the value in column A of the same row we just looked at above AND copy some other data from the "Data" sheet to the new sheet as well. head spinning!
Example of the "Data" sheet: Well, it wouldn't let me insert an image because I don't have enough reputation points. Here's a link to the example image. CLICK HERE
The first new sheet created would be named "BBB" (taken from B2). Then, using other data from the first row, we want to find the sheet named "Brown" (taken from E2) that already exists in the workbook. Copy everything from "Brown" and paste it to the new "BBB" sheet
Now, while still on this row in the loop, having just populated the new sheet, we want to now copy the value from the A2 ("Big Brown Ball") to the new sheet ("BBB") at F2 and fill it down the entire range for that column (as if I double-clicked the cell handle).
THEN, at long last, we can loop back to the next row of "Locations" sheet and repeat the same process until we run out of populated rows.
I'm so sorry at the complexity of this explanation, but that's the exact reason why I found myself needing to ask the question! I just couldn't wrap my head around it.
Any help would be supremely appreciated!!!
EDIT: The responder below asked good questions as his head was spinning too! My answers may be helpful to others as well.
You can try this:
Sub Test()
Dim r As Range, c As Range, lrow As Long
With ThisWorkbook
Set r = .Sheets("Data").Range("B2", "B4")
For Each c In r
.Sheets(c.Offset(0, 3).Value).Copy After:=.Sheets(.Sheets.Count)
With ActiveSheet
.Name = c
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("F2:F" & lrow).Value = c.Offset(0, -1)
End With
Next
End With
End Sub
Is this what you're trying? HTH.
I am looking to do the following:
When pressing a button, the Macro should be activated.
The Macro selects Column H in Sheet 2 (same workbook). Not the whole column, just until data goes. The last line with data can be determined if after that line the next 10 lines are empty.
For this selection, the "General" format is applied to every cell.
After this, the same runs through for Column G.
The Macro ends.
I think it should be easily possible, but I am especially struggling with the "determining last line with data" part, as if applied to the whole column the PC massively slows down.
Then, I am unsure where I should put the code (Sheet, ThisWorkbook, Module) as best practice.
There are other ways to do this, but the easiest generally is to choose a LARGE row number (will there ever be more than 20,000 rows?) and navigate upwards.
Range("H2", Range("H20000").End(xlUp)).NumberFormat = "General"
Range("G2", Range("G20000").End(xlUp)).NumberFormat = "General"
But you can also just format the entire columns:
Range("G:H").NumberFormat = "General"
this doesn't (these days) impact the size of the file.
You want to click a button of the sheet to run the macro, so you could use a Form Controls, Button and the code would then be in a standard module.
Added (from my comment):
This would work:
Application.Intersect(ActiveSheet.UsedRange, Range("G:H")).NumberFormat = "General"
but it requires some error-handling, just in case these ranges don't intersect.
Responding to 2 further questions in comments:
Worksheets("Whatever").Activate
If NumberFormat applied to the column doesn't work then there must be something else going on that interferes - or perhaps the data was faulty when imported(?). Try:
Application.CalculateFull
or just use the specific range:
Range("..").Calculate
If this doesn't work then you may have to copy the data to a blank column and delete the old column. Or perhaps copy and paste (maybe values) into the same range.
You can format all cells in columns G and H as general without selecting the range or the sheet. You should always avoid selecting anything in your VBA code.
The de facto standard way of finding the last row with data is to start at bottom of the sheet and go up from there.
The following finds the last cell with data on sheet2 in both columns G and H. We use the greatest of the two to set the range we will use to apply general formatting.
Sub GeneralFormatForAllPopulatedCells()
Dim wb As Workbook
Dim ws As Worksheet
Dim g As Long, h As Long
Dim lastRow As Long
Dim rng As Range
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet2")
g = ws.Range("G" & ws.Rows.Count).End(xlUp).Row
h = ws.Range("H" & ws.Rows.Count).End(xlUp).Row
If g > h Then
lastRow = g
Else
lastRow = h
End If
Set rng = ws.Range("G1:H" & lastRow)
rng.NumberFormat = "General"
End Sub
You should place your code in a module, and make sure that Option Explicit appears at the top of the module so that variable declaration is required. You can turn this on for all modules by opening the options dialog from within the VBA editor: Tools --> Options and then checking the box next to Require Variable Declaration.
I'm trying to create a simple macro that copys the two first numbers in each cell in a column and printing them in a different column. This is in a excel-document with more than 1 worksheet. I've tried for a while to write the code, but with no prior experience, I have a hard time figuring out what to do. I know the "left()"-function is able to do this, but I don't know how I define which column to draw data from and to which column it will be printed. Any help will be greatly appreciated.
With no prior experience to writing VBA code, I am going to reccommend you stick to the formula method of doing. Honestly, even if you were familiar with VBA, you might still opt to use the formula.
A formula is put into the actual cell you want the value to be copied.
=LEFT(sourceCell, #of characters you want)
This is how it would look:
=LEFT(Sheet1!A1, 2)
Think of it as saying "this cell shall equal the first n characters in cell OO, starting from the left".
Once you are done with your formula, if you don't need it to be binded to the source anymore (if the sourceCell changes, so does the cell with the LEFT formula), you can highlight the cells, Ctrl + C to copy, then right-click and select Paste Special. Then select VALUE and hit OK and now the cells are hard-coded with the value they were showing, as if you typed it yourself.
Once you master using formulas, the next step is VBA. Don't go confusing yourself by jumping into VBA and writing code for ranges, etc. if you aren't comfortable with using =LEFT yet. One step at a time, and you'll be a pro before you know it. :)
Here is a quick sample sub to get you started:
Public Sub LeftSub()
Dim SourceRange As Range, DestinationRange As Range, i As Integer
'Define our source range as A1:A10 of Sheet1
Set SourceRange = Sheet1.Range("A1:A10")
'Define our target range where we will print.
'Note that this is expected to be of same shape as source
Set DestinationRange = Sheet1.Range("B1:B10")
'Iterate through each source cell and print left 2 bits in target cell
For i = 1 To SourceRange.Count
DestinationRange(i, 1).Value = Left(SourceRange(i, 1).Value, 2)
Next i
End Sub
How about
Sub foo()
Dim cell As Range
Dim sourceRange As Range
'//define the source column - looks for contiguous downward data from A1;
Set sourceRange = Range(Sheets("Sheet1").Range("A1"), Selection.End(xlDown))
'//iterate each cell
For Each cell In sourceRange
If IsEmpty(cell.Value) Then Exit For
'//example to place the value in corresponding row of column B in sheet 2
Sheets("Sheet2").Range("B" & cell.Row).Value = Left$(cell.Value,2)
Next
End Sub
Or an equivalent formula (in the destination cell)
=LEFT(Sheet1!A1,2)