VBA Array doesn't work? - vba

I have this practice file with 5 order prices. The goal is to add $20 to each of the record and have a message box to display the result.
Here is the data:
My code is this:
Sub TotalDelivery()
Dim curDelCharge As Currency
Dim curTotal(4)
Dim i As Integer
Worksheets("Sheet1").Range("B10").Activate
Const curDelCharge = 20
For i = 0 To 4
curTotal(i) = ActiveCell.Offset(i, 1).Value + curDelCharge
MsgBox (curTotal(i))
Next i
End Sub
However the message box only displays 20 which is only my curDelCharge value.
To debug, I change the msgbox code into:
MsgBox (ActiveCell.Offset(i, 1).Value)
The return value is blank which means the code doesn't read my ActiveCell value. Why is that?
Thanks in advance!

This line:
curTotal(i) = ActiveCell.Offset(i, 1).Value + curDelCharge
should instead be:
curTotal(i) = ActiveCell.Offset(i, 0).Value + curDelCharge
Putting a "1" will move the offset 1 column to the right, which you don't want.

Sub TotalDelivery()
Dim curTotal(4)
Dim i As Integer
Dim rngCellsToChange As Range 'range of cells you are targeting
Dim rCell As Range 'individual cell in collection of cells. See alternative solution below
'You can refer to cells directly, without activating them.
'You are highly discouraged to use Activate or Select methods.
'Use ThisWorkbook to explicitly tell VBA, which workbook you are targeting
Set rngCellsToChange = ThisWorkbook.Worksheets("Sheet1").Range("B10:B14")
Const curDelCharge = 20
For i = 0 To 4
curTotal(i) = rngCellsToChange(i + 1).Value + curDelCharge
MsgBox (curTotal(i))
Next i
'Alternatively, you can use the Range object to loop through all it's cells, like so:
For Each rCell In rngCellsToChange
MsgBox rCell.Value + curDelCharge
Next
End Sub

Related

Check that all values in a range are identical

I need to display a message box when all the values in a range on my spreadsheet are zero. Currently I am using the following code:
Dim Cell As Range
For Each Cell In Range("E17:E25")
If Cell.Value = "0" Then
MsgBox ("If hardware is required, please manually populate the corresponding sections.")
End If
Next
The message is displayed, however it is shown 9 times (for each of the cells in the range). What I need is to check if all the values in the range E17:E25 are zero, and then display only one message box. Any ideas?
Thanks.
You want to know if all the values are 0? You could just do
If WorksheetFunction.Sum(Range("E17:E25")) = 0 Then MsgBox ("If hardware is required, please manually populate the corresponding sections.")
No need for loops.
Edit: If you want to check for any other number, and if all cells are that number, you can do this:
Sub t()
Dim rng As Range
Dim myNum as Long
myNum = 1
Set rng = Range("B3:B6")
If WorksheetFunction.CountIf(rng, myNum) = rng.Count Then MsgBox ("All the same!")
End Sub
And cause there are infinite ways to skin a cat here is another approach.
Dim Cell As Range
Dim ZeroCount As Integer
Dim CellCount As Integer
ZeroCount = 0
CellCount = 0
For Each Cell In Range("E17:E25")
CellCount = CellCount + 1
If Cell.Value = 0 Then ZeroCount = ZeroCount + 1
Next Cell
If ZeroCount = CellCount Then MsgBox ("If hardware is required, please manually populate the corresponding sections.")
To test that:
The range doesn't contain any empty values
All cells are the same
function
Function SameRange(rngIn As Range) As Boolean
If Application.CountA(rngIn) = rngIn.Cells.Count Then SameRange = (Application.CountIf(rngIn, rngIn.Cells(1).Value) = rngIn.Cells.Count)
End Function
test
Sub test()
MsgBox SameRange([d1:d5])
End Sub
'something like this
Dim isDataPresent as boolean
isDataPresent = true
for each Cell in Range(....)
if cell.value = "0" then
isDataPresent = false
exit for
end if
next
if not isDataPresent then
show message box here
end if

Highlight values on Sheet1 if matched on Sheet2

I'm looking for a way to highlight cells in sheet1 if they match the value in sheet2. Here is the code I have, there aren't any errors coming up but it does nothing. Basically I thought a Do while loop to go through all the records until it hit a blank and then it would read the cell value selected by my offset and compare it to the next sheets cell value while staying on the same row, and if it matched it would highlight on sheet 1 but if it didn't it would move on. Let me know how much I'm off here as I don't have much VBA knowledge. Thanks.
Public Sub RoundedRectangle1_Click()
Dim resource As Range
Dim register As Range
Dim cancel As Range
Set resource = Worksheets("Resource List1").Cells(2, 4)
Set register = Worksheets("Registered List").Cells(2, 1)
Set cancel = Worksheets("Cancelled List").Cells(2, 1)
Call findRegister(resource, register)
End Sub
Public Sub findRegister(ByRef resource As Range, ByRef register As Range)
Dim i As Integer
i = 0
Do While resource.Offset(i, 3) <> ""
If resource.Offset(i, 3).Value = register.Range("A2").Value Then
resource.Offset(i, 3).Cells.Interior.ColorIndex = 37
End If
i = i + 1
Loop
End Sub
Your code is essentially correct, but I think you're having trouble with referencing the right cells. A good debugging technique would be to add .Cells.Interior.ColorIndex = 4 or something similar in your code to see visually whether you're referencing the proper cells. You can also put "F5", "F8", and breakpoints to good use in figuring out what's wrong. See http://www.excel-easy.com/vba/examples/debugging.html if you've never used these.
For example:
Do While resource.Offset(i, 3) <> "" '<--Insert a breakpoint on this line,
'then press "F8" to make sure the
'code inside your Do While loop is
'being executed
resource.Offset(i, 3).Cells.Interior.ColorIndex = 4
register.Range("A2").Cells.Interior.ColorIndex = 6
If resource.Offset(i, 3).Value = register.Range("A2").Value Then
resource.Offset(i, 3).Cells.Interior.ColorIndex = 40
End If
i = i + 1
Loop
Maybe something as simple as this . . . .
Sub Compare2Shts()
For Each Cell In Worksheets("CompareSheet#1").UsedRange
If Cell.Value <> Worksheets("CompareSheet#2").Range(Cell.Address) Then
Cell.Interior.ColorIndex = 3
End If
Next
For Each Cell In Worksheets("CompareSheet#2").UsedRange
If Cell.Value <> Worksheets("CompareSheet#1").Range(Cell.Address) Then
Cell.Interior.ColorIndex = 3
End If
Next
End Sub

Excel VBA delete entire row if cell in column D is empty

Can anyone walk me through how to write a script to delete the entire row if a cell in column D = "" on sheet 3 in range D13:D40.
Also, how to prevent the user from accidentally running the script again once those cells in the range are already deleted and other cells are now on the D13:D40 range?
Solution: This is working for me:
Sub DeleteRowsWithEmptyColumnDCell()
Dim rng As Range
Dim i As Long
Set rng = ThisWorkbook.ActiveSheet.Range("D13:D40")
With rng
' Loop through all cells of the range
' Loop backwards, hence the "Step -1"
For i = .Rows.Count To 1 Step -1
If .Item(i) = "" Then
' Since cell is empty, delete the whole row
.Item(i).EntireRow.Delete
End If
Next i
End With
End Sub
Explanation: Run a for loop through all cells in your Range in column D and delete the entire row if the cell value is empty. Important: When looping through rows and deleting some of them based on their content, you need to loop backwards, not forward. If you go forward and you delete a row, all subsequent rows get a different row number (-1). And if you have two empty cells next to each other, only the row of the first one will be deleted because the second one is moved one row up but the loop will continue at the next line.
No need for loops:
Sub SO()
Static alreadyRan As Integer
restart:
If Not CBool(alreadyRan) Then
With Sheets("Sheet3")
With .Range("D13:D40")
.AutoFilter 1, "="
With .SpecialCells(xlCellTypeVisible)
If .Areas.Count > 1 Then
.EntireRow.Delete
alreadyRan = alreadyRan + 1
End If
End With
End With
.AutoFilterMode = False
End With
Else
If MsgBox("procedure has already been run, do you wish to continue anyway?", vbYesNo) = vbYes Then
alreadyRan = 0
GoTo restart:
End If
End If
End Sub
Use AutoFilter to find blank cells, and then use SpecialCells to remove the results. Uses a Static variable to keep track of when the procedure has been run.
Here's my take on it. See the comments in the code for what happens along the way.
Sub deleterow()
' First declare the variables you are going to use in the sub
Dim i As Long, safety_net As Long
' Loop through the row-numbers you want to change.
For i = 13 To 40 Step 1
' While the value in the cell we are currently examining = "", we delete the row we are on
' To avoid an infinite loop, we add a "safety-net", to ensure that we never loop more than 100 times
While Worksheets("Sheet3").Range("D" & CStr(i)).Value = "" And safety_net < 100
' Delete the row of the current cell we are examining
Worksheets("Sheet3").Range("D" & CStr(i)).EntireRow.Delete
' Increase the loop-counter
safety_net = safety_net + 1
Wend
' Reset the loop-counter
safety_net = 0
' Move back to the top of the loop, incrementing i by the value specified in step. Default value is 1.
Next i
End Sub
To prevent a user from running the code by accident, I'd probably just add Option Private Module at the top of the module, and password-protect the VBA-project, but then again it's not that easy to run it by accident in the first place.
This code executes via a button on the sheet that, once run, removes the button from the worksheet so it cannot be run again.
Sub DeleteBlanks()
Dim rw As Integer, buttonID As String
buttonID = Application.Caller
For rw = 40 To 13 Step -1
If Range("D" & rw) = "" Then
Range("D" & rw).EntireRow.Delete
End If
Next rw
ActiveSheet.Buttons(buttonID).Delete
End Sub
You'll need to add a button to your spreadsheet and assign the macro to it.
There is no need for loops or filters to find the blank cells in the specified Range. The Range.SpecialCells property can be used to find any blank cells in the Range coupled with the Range.EntireRow property to delete these. To preserve the run state, the code adds a Comment to the first cell in the range. This will preserve the run state even if the Workbook is closed (assuming that it has been saved).
Sub DeleteEmpty()
Dim ws As Excel.Worksheet
Set ws = ActiveSheet ' change this as is appropriate
Dim sourceRange As Excel.Range
Set sourceRange = ws.Range("d13:d40")
Dim cmnt As Excel.Comment
Set cmnt = sourceRange.Cells(1, 1).Comment
If Not cmnt Is Nothing Then
If cmnt.Text = "Deleted" Then
If MsgBox("Do you wish to continue with delete?", vbYesNo, "Already deleted!") = vbNo Then
Exit Sub
End If
End If
End If
Dim deletedThese As Excel.Range
On Error Resume Next
' the next line will throw an error if no blanks cells found
' hence the 'Resume Next'
Set deletedThese = sourceRange.SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not deletedThese Is Nothing Then
deletedThese.EntireRow.Delete
End If
' for preserving run state
If cmnt Is Nothing Then Set cmnt = sourceRange.Cells(1, 1).AddComment
cmnt.Text "Deleted"
cmnt.Visible = False
End Sub
I've recently had to write something similar to this. I'm not sure that the code below is terribly professional, as it involves storing a value in cell J1 (obviously this can be changed), but it will do the job you require. I hope this helps:
Sub ColD()
Dim irow As long
Dim strCol As String
Sheets("sheet2").Activate
If Cells(1, 10) = "" Then
lrun = " Yesterday."
Else: lrun = Cells(1, 10)
End If
MsgBox "This script was last run: " & lrun & " Are you sure you wish to continue?", vbYesNo
If vbYes Then
For irow = 40 To 13 step -1
strCol = Cells(irow, 4).Value
If strCol = "" Then
Cells(irow, 4).EntireRow.Delete
End If
Next
lrun = Now()
Cells(1, 10) = lrun
Else: Exit Sub
End If
End Sub

How do I make value static in last row value?

Here is the code below:
Public n as Long ' <--above sub procedure
With Sheets("Sheet1").Range("A6").Offset(n, 0)
If n = 0 Then
.Value = 1
Else
.Value = .Parent.Range(.Address).Offset(-1, 0) + 1
End If
n = n + 1
End With
(See pic below) If I delete 4 then click command button again it just reset back to 1. I want to make it static so even I deleted the last value of row it still continue increment from the last value.
Store number
1
2
3
4
Try this:
Sub Test()
Dim trow As Long
With Sheets("Sheet1") '~~> change to suit
trow = .Range("A:A").Find(vbNullString, [A5]).Row
With .Range("A" & trow)
If trow = 6 Then .Value = 1 _
Else .Value = .Offset(-1, 0).Value + 1
End With
End With
End Sub
Above code finds the first blank cells. If it is A6 it assigns a value of 1.
Otherwise it assigns previous cell value plus 1. Is this what you're trying?
Edit1: Explanation
trow = .Range("A:A").Find(vbNullString, [A5]).Row
This finds the first empty row in Column A starting A5.
[A5] is used to return Range("A5") object. So it can also be written as:
trow = .Range("A:A").Find(vbNullString, .Range("A5")).Row
We used a VBA vbNullString constant as What argument in Range Object Find Method.
Find Method returns a Range Object so above can be written also like this:
Sub Test()
Dim r As Range
With Sheets("Sheet1") '~~> change to suit
Set r = .Range("A:A").Find(vbNullString, [A5])
With r
If .Row = 6 Then .Value = 1 _
Else .Value = .Offset(-1, 0).Value + 1
End With
End With
End Sub
What your asking for, a button with memory doesn't sound neatly solvable using just VBA.
You could potentially have a list on a hidden sheet that gets a value added to it each time the commandButton is pressed and it writes the max of the list values back to the target cell?
Alternatively you could investigate using a scrollbar from the form control section of the developer tab with a link to your target cell. I often use this technique for interactive sheets.
Named Range Method
Public sub btnPress
dim val as long
val = Range("PreviousCellValue")
set Range("PreviousCellValue") = val+1
Sheets("Sheet1").Range("A6").Offset(n, 0).value = Range("PreviousCellValue")
End sub btnPress

Excel VBA Get hyperlink address of specific cell

How do I code Excel VBA to retrieve the url/address of a hyperlink in a specific cell?
I am working on sheet2 of my workbook and it contains about 300 rows. Each rows have a unique hyperlink at column "AD". What I'm trying to go for is to loop on each blank cells in column "J" and change it's value from blank to the hyperlink URL of it's column "AD" cell. I am currently using this code:
do while....
NextToFill = Sheet2.Range("J1").End(xlDown).Offset(1).Address
On Error Resume Next
GetAddress = Sheet2.Range("AD" & Sheet2.Range(NextToFill).Row).Hyperlinks(1).Address
On Error GoTo 0
loop
Problem with the above code is it always get the address of the first hyperlink because the code is .Hyperlinks(1).Address. Is there anyway to get the hyperlink address by range address like maybe sheet1.range("AD32").Hyperlinks.Address?
This should work:
Dim r As Long, h As Hyperlink
For r = 1 To Range("AD1").End(xlDown).Row
For Each h In ActiveSheet.Hyperlinks
If Cells(r, "AD").Address = h.Range.Address Then
Cells(r, "J") = h.Address
End If
Next h
Next r
It's a bit confusing because Range.Address is totally different than Hyperlink.Address (which is your URL), declaring your types will help a lot. This is another case where putting "Option Explicit" at the top of modules would help.
Not sure why we make a big deal, the code is very simple
Sub ExtractURL()
Dim GetURL As String
For i = 3 To 500
If IsEmpty(Cells(i, 1)) = False Then
Sheets("Sheet2").Range("D" & i).Value =
Sheets("Sheet2").Range("A" & i).Hyperlinks(1).Address
End If
Next i
End Sub
My understanding from the comments is that you already have set the column J to a string of the URL. If so this simple script should do the job (It will hyperlink the cell to the address specified inside the cell, You can change the cell text if you wish by changing the textToDisplay option). If i misunderstood this and the string is in column AD simply work out the column number for AD and replace the following line:
fileLink = Cells(i, the number of column AD)
The script:
Sub AddHyperlink()
Dim fileLink As String
Application.ScreenUpdating = False
With ActiveSheet
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 4 To lastrow
fileLink = Cells(i, 10)
.Hyperlinks.Add Anchor:=Cells(i, 10), _
Address:=fileLink, _
TextToDisplay:=fileLink
Next i
End With
Application.ScreenUpdating = True
End Sub
Try to run for each loop as below:
do while....
NextToFill = Sheet2.Range("J1").End(xlDown).Offset(1).Address
On Error Resume Next
**for each** lnk in Sheet2.Range("AD" & Sheet2.Range(NextToFill).Row).Hyperlinks
GetAddress=lnk.Address
next
On Error GoTo 0
loop
This IMO should be a function to return a string like so.
Public Sub TestHyperLink()
Dim CellRng As Range
Set CellRng = Range("B3")
Dim HyperLinkURLStr As String
HyperLinkURLStr = HyperLinkURLFromCell(CellRng)
Debug.Print HyperLinkURLStr
End Sub
Public Function HyperLinkURLFromCell(CellRng As Range) As String
HyperLinkURLFromCell = CStr(CellRng.Hyperlinks(1).Address)
End Function