how to call function to modify a variable vba - vba

What my code does is it takes a value froma nother workbook and copies and paste it in another workbook. What I want now however is a something that would remove the last 3 figures to have a final number in thousand dollars instead of dollars.
It would work fine if I were to build my function around my "DSA.Worksheet.Value..." but it would take time because I have quite a few of those.
When I call my function on the DSA.worksheet cell it doesnt work, even if I call it like that:
Call number_right (DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round (hello.Worksheets("getthis"").Range("I72").Value, -3)
Sub test()
DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
DSA.Worksheets(1).Range("G10").Value = Call number_right(DSA.Worksheets(1).Range("G10").Value)
End Sub
Function number_right(n As Variant) As Variant
If Len(n) > 3 Then n = Left((n), Len(n) - 3)
End Function

Maybe this:
Sub test()
Dim DSA As Workbook
Dim rngValue As Range
Set DSA = ActiveWorkbook 'May need to change this line if DSA is not already open
Set rngValue = DSA.Worksheets(1).Range("G10")
Set rngValue = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
If Len(rngValue) > 3 Then
Set rngValue = number_right(rngValue)
End If
End Sub
Function number_right(n As Range) As Variant
number_right = Left(n, Len(n) - 3)
End Function

Related

# VALUE Error while executing UDF VBA Function on formatted cells

I wrote the following function to check if the prerequisites for my Excel row are satisified.
Public Function PREREQUISITESOK(prerequisites As Range) As String
Dim cw As Worksheet
Dim prerequisite_cell As Range
Dim prerequisite_cell_txt As String
Dim training_id_cell As Range
Dim no_groups_cell_to_compare As Range
Dim no_groups_cell_to_check As Range
Dim training_id_cell_txt As String
Dim training_id_cell_row_n As Integer
Dim n As Integer
Application.Volatile
PREREQUISITESOK = "OK"
Set cw = Sheets("4c.Trainings OSS")
Set training_id = cw.Range("$B$11:$B$34")
Set no_groups_cell_to_compare = cw.Range("J" & CStr(prerequisites.Row))
For Each prerequisite_cell In prerequisites.Cells
prerequisite_cell_txt = prerequisite_cell.Text
If prerequisite_cell_txt = "" Then
Exit For
Else
For Each training_id_cell In training_id.Cells
training_id_cell_txt = training_id_cell.Text
If training_id_cell_txt = prerequisite_cell_txt Then
training_id_cell_row_n = training_id_cell.Row
Set no_groups_cell_to_check = cw.Cells(training_id_cell_row_n, no_groups_cell_to_compare.Column)
If no_groups_cell_to_check.Value < no_groups_cell_to_compare.Value Then
PREREQUISITESOK = "NOT OK"
Exit Function 'It is enough for us that one prerequisite is not satisfied so we can exit the function
Else
PREREQUISITESOK = "OK"
End If
Exit For 'Training IDs are unique so if we find the right Training ID then we may exit the loop
End If
Next training_id_cell
End If
Next prerequisite_cell
End Function
Note that the prerequisites range is inline formatted.
The function that I wrote is supposed to return String value so I completely do not understand why am I getting #VALUE! error.
What is interesting that if I clear formatting from the prerequisites cells that are used as arguments of the function then #VALUE! error disappears.
Do you have any idea why this happens?

How to keep macro looping on a specified worksheet even when I switch worksheets in the same workbook

I tried to look for answers to this question but to no avail. I need this Macro to run on a specific worksheet called General on a specific workbook. The purpose, is to let the cell I24 be multiplied by 1.0003 every minute (which makes it a loop as far as I know). The below code only works when I have the General sheet opened. It stops looping when I switch to another worksheet.
Also, I want the macro to run automatically open opening the workbook, regardless of the General sheet being selected, so that I24 on the General sheet keeps getting multiplied without being redirected to the sheet. Just so you know, I have that cell referenced on various other sheets in the workbook, that is why I need the macro constantly running. Below is my code (It may not be at its optimum condition since I am very new to VBA):
Sub auto_open()
WshtNames = Array("General")
Application.ScreenUpdating = False
Dim num As Long
num = Sheets("General").Range("I24").Value
num = num * 1.0003
Range("I24").Value = num
Application.OnTime Now + TimeValue("00:01:00"), "auto_open"
Application.ScreenUpdating = True
End Sub
Thank you, I really appreciate your assistance.
Analyzing your code and making some suggestions to improve and remove unnecessary code
Switching Application.ScreenUpdating doesn't make much sense in this specific case, because there is only one update in Range("I24"). Therefore no gain if you turn it off.
There is only an advantage if you have many updates, so that they get performed all at once when switching Application.ScreenUpdating = True.
Use Option Explicit. This forces you to declare all your variables properly.
You set WshtNames but never use it, so this line can be removed.
Use Worksheets instead of Sheets unless you really need to use Sheets (Sheets also contains charts not only worksheets).
If num is Long then it can only contain integer/whole numbers. Therfore if you multiply num = num * 1.0003 it will automatically cast into Long which is the same result as num = num and that means it doesn't change anything. You will need to use at least Double or Decimal here.
You didn't specify a worksheet for the Range("I24").Value = num so Excel assumes that the range is in the active sheet. This is why your code fails when you select another sheet. Never let VBA guess the worksheet always specify the correct one Worksheets("General").Range("I24").Value = num.
So all together we can change your code from …
Sub auto_open()
WshtNames = Array("General") '(3) can be removed because WshtNames is never used
Application.ScreenUpdating = False '(1) dosn't make much sense
Dim num As Long '(5) wrong data type
num = Sheets("General").Range("I24").Value '(4) use worksheets
num = num * 1.0003 'see (5)
Range("I24").Value = num '(6) Always specify a worksheet
Application.OnTime Now + TimeValue("00:01:00"), "auto_open"
Application.ScreenUpdating = True
End Sub
Into this …
Option Explicit
Public Sub auto_open()
Dim num As Double
With Workheets("General") 'note we use a with statement to specify the sheet for the ranges (starting with a dot now!)
num = .Range("I24").Value
num = num * 1.0003
.Range("I24").Value = num
End With
Application.OnTime Now + TimeValue("00:01:00"), "auto_open"
End Sub
Or even shorter, because we don't need the num variable for that short calculation:
Option Explicit
Public Sub auto_open()
With Workheets("General") 'note we use a with statement to specify the sheet for the ranges (starting with a dot now!)
.Range("I24").Value = .Range("I24").Value * 1.0003
End With
Application.OnTime Now + TimeValue("00:01:00"), "auto_open"
End Sub
This part of the code has wrong logic:
Dim num As Long
num = Sheets("General").Range("I24").Value
num = num * 1.0003
Long is a whole number by specification. If you multiply it by 1.0003 it is the same as if it is multiplied by 1. Consider using Double instead.
Or Decimal, for better precision:
Dim num as Double
num = Sheets("General").Range("I24")
num = CDec(num * 1.0003)
You must set your cell as a variable.
Dim myCell as Range
Set myCell = ThisWorkbook.Worksheets("General").Range("I24")
and in the code:
myCell.Value = num
EDIT:
The whole code:
Sub auto_open()
WshtNames = Array("General")
Application.ScreenUpdating = False
Dim myCell As Range
Set myCell = ThisWorkbook.Worksheets("General").Range("I24")
myCell = myCell * 1.0003
Application.OnTime Now + TimeValue("00:01:00"), "auto_open"
Application.ScreenUpdating = True
End Sub

Custom Function Entering the Result Array into a Different Cell

I have created my own function to determine count the values in between to given values in increments of 30 as seen here
Function InBetween(First As Integer, Last As Integer)
Dim i As Long, F As String, a() As String
F = First
For i = First + 30 To Last Step 30
F = F & "|" & i
Next i
InBetween = F
End Function
When I use this function, I currently have it returning the result array in the cell the formula was entered into in the format of "1|2|3|4". Is there a way I can get this array to populate into the cell below the one containing the formula?
Note: I don't want the formula in the cell as I need to refer to the cell in a future equation that will use the result and not the equation.
This was surprisingly difficult. At first I tried calling a sub from the function to affect the cell below using application.caller but this always returned a #value error. It seems a UDF can't run anything that affects the worksheet.
Eventually I came up with this:
Create a worksheet change event by pasting this into the worksheet object in vb:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Left(Target.Offset(-1, 0).Formula, 10) = "=InBetween" Then Call DoX(Target.Offset(-1, 0), InBetween(10, 60))
On Error GoTo 0
End Sub
Then paste this into a module
Sub DoX(r As Range, val As String)
Sheets(r.Parent.Name).Cells(r.Row, r.Column) = ""
Sheets(r.Parent.Name).Cells(r.Row + 1, r.Column) = val
End Sub
Then use your function as normal, but remember to hit return after you enter it so the active cell is the cell below where you entered the formula.

Why is assigning the Value property of cell causing code to end aburptly?

Private Sub FillRow(programCell As Range, storedProgramCell As Range)
Dim counter As Integer
For counter = 3 To 9
Dim cellOffset As Integer
cellOffset = counter - 3
Dim currentStoredCell As Range
Set currentStoredCell = storedProgramCell.Offset(0, cellOffset)
Dim value As String
value = currentStoredCell.value
Dim currentTargetCell As Range
Set currentTargetCell = programCell.Offset(0, cellOffset)
MsgBox currentStoredCell.value 'Works correctly, prints correct value
currentTargetCell.value = value
Next counter
End Sub
The line:
currentTargetCell.value = value
causes the code to stop executing, with no error.
I added the expression to my watch list, then stepped through the routine. The expression was seen as a Boolean:
This makes me think the expression is being viewed as a comparison, and the program abruptly ends since the returned Boolean is not being stored or used anywhere. I wouldn't doubt if I were wrong though.
I'm new to VBA, struggling to debug my program, so please forgive me if this is a petty mistake. I couldn't find any sources online that explains this problem.
Replace your subroutine with following code:
Private Sub FillRow(Dst As Range, Src As Range)
Dim x As Integer
Dim v As Variant
Dim Srcx As Range
Dim Dstx As Range
Debug.Print "FillRow"
Debug.Print Src.Address
Debug.Print Dst.Address
Debug.Print "Loop"
For x = 0 To 6
Debug.Print x
Set Srcx = Src.Offset(0, x)
Debug.Print Srcx.Address
v = Srcx.Value
Debug.Print TypeName(v)
Set Dstx = Dst.Offset(0, x)
Debug.Print Dstx.Address
Dstx.Value = v
Next
Debug.Print "Completed"
End Sub
Run and post in your question Immediate window output.
Value is a reserved word, even if vba does not raise an error on this name, you should not use it. Name it something else. Also, try setting it as a variant.

Excel VBA - Dynamically supply ranges to Union method

User has defined named ranges to print in Excel.
I am reading these ranges into a VBA array. Is there a way to supply the range names to the Union method to set non-contiguous print ranges.
For example, something like:
ActiveSheet.PageSetup.PrintArea = Union(Range(array(1)), Range(array(2))).Address
The number of ranges held in the array can vary. I've experimented with looping through the array and building a string variable, but no success.
Any help would be appreciated.
You'll have to substitute the actual range names or objects in the statement, but here is how to use the Union function to set a PrintArea:
Sub foo()
Dim setup As PageSetup
Set setup = ActiveSheet.PageSetup
setup.PrintArea = Union(Range("MyRange1"), Range("MyRange2")).Address
End Sub
What I'm actually looking for is a method to construct the Union statement using range names that are held in an array
OK, then use the above method and a custom function to construct the Union in a loop:
Sub foo()
Dim setup As PageSetup
Dim RangeArray(1) As Range
Set setup = ActiveSheet.PageSetup
Set RangeArray(0) = Range("MyRange1")
Set RangeArray(1) = Range("MyRange2")
setup.PrintArea = GetUnion(RangeArray)
End Sub
Function GetUnion(arr As Variant) As String
Dim itm As Variant
Dim ret As Range
For Each itm In arr
If Not ret Is Nothing Then
Set ret = Union(ret, itm)
Else
Set ret = itm
End If
Next
If Not ret Is Nothing Then
GetUnion = ret.Address
Else
GetUnion = "" 'May cause an error...
End If
End Function