I have a problem:
Public Sub ChangeRow(Column As String, Value As String, id As Integer)
For i = 4 To 15
For Each rw In Worksheets(i).Rows
If Worksheets(i).Range("A" & rw.row).Value = id Then
Dim row As Integer
**row = getRow(id, i)**
MsgBox (row)
If Worksheets(i).Range(Column & rw.row).Value <> Value Then
Worksheets(i).Range(Column & rw.row) = Value
End If
Exit For
End If
Next rw
Next i
End Sub
Function getRow(id As Integer, Sheet As Integer) As Integer
For Each rw In Worksheets(Sheet).Rows
If Worksheets(Sheet).Range("A" & rw.row).Value = id Then
getRow = rw.row
End If
Next rw
End Function
Change Row Works fine... its just when I add 'row = getRow(id, i)' to the mix it throws a ByRef mismatch error??
This is a great example as to why using Option Explicit is a great practice.
Add Option Explicit to the very top of your worksheet module, outside your macro.
This forces you to declare all variables used in your subs, and also can help catch typos in your variable names.
This would catch that i is not declared. What's therefore happening is VBA/Excel by default will set i to be Variant.
Then, when you hit row = getRow(id, i), you're basically passing row = getRow([integer], [Variant]). But that sub is expecting getRow([integer],[integer])...hence your Type Mismatch error.
So, as mentioned, just do Dim i as Integer at the top of the ChangeRow sub.
(or, for long run VBA use Long instead of Integer).
In the function, you need to declare "Sheet" as Worksheet object
Function getRow(id As Integer, Sheet As Worksheet) As Integer
Related
I've seen how to edit a hyperlink - but I need to add a hyperlink when used as a custom formula.
I get a #VALUE error and I'm not sure why. Does anyone have any ideas why I get a #VALUE error when I try to use it in a sheet as =testit39()
Public Function testit39() As String
Application.Volatile
Dim rng As range, milestoneinfo As String, milestonesymbol As String
Set rng = Application.Caller
milestoneinfo = "info"
milestonesymbol = "symbol"
If rng.Hyperlinks.Count > 0 Then
rng.Hyperlinks(1).address = ""
rng.Hyperlinks(1).screentip = milestoneinfo
Else
ThisWorkbook.ActiveSheet.Hyperlinks.Add Anchor:=rng, _
address:="", _
screentip:=milestoneinfo
rng.Hyperlinks(1).screentip = milestoneinfo
End If
testit39 = milestonesymbol
End Function
I have found a way that is not complicated thanks to this wonderful tutorial..
http://optionexplicitvba.blogspot.co.uk/2011/04/rollover-b8-ov1.html
So essentially you put it in a hyperlink and you're free to do as you please..
=hyperlink(testit39(), "Wahoo it works!")
UDFs (User-defined functions) are only allowed to return a value, they may not e.g. affect other cells or do other manipulations.
When you single-step through your code, you'll see that it aborts on the ...Hyperlinks.Add-line (and returns an error value).
Following VBA Sub code snippet allows adding new Hyperlink, or editing existing in a specified sample cell "A1" (non-essential part of your code has been removed for better clarity):
Public Sub AddOrEditHyperlink(milestonesymbol As String)
Dim rng As Range, milestoneinfo As String
'test range
Set rng = Range("A1")
'sample properties
milestoneinfo = "info"
'if Hyperlink exists, display "Edited"
If rng.Hyperlinks.Count > 0 Then
rng.Hyperlinks(1).Address = ""
rng.Hyperlinks(1).ScreenTip = milestoneinfo
rng.Hyperlinks(1).TextToDisplay = "Edited Hyperlink"
Else 'if Hyperlink does not exist, add and display "New"
rng.Hyperlinks.Add _
Anchor:=rng, _
Address:="", _
ScreenTip:=milestoneinfo, _
TextToDisplay:="New Hyperlink"
End If
End Sub
You can call this Sub from the Function that you can define (UDF) corresponding to the rest of business logic of your project (which is a bit unclear articulated):
Public Function testit39() As String
Application.Volatile
Dim rng As Range, milestoneinfo As String, milestonesymbol As String
Call AddOrEditHyperlink("some Symbol")
testit39 = milestonesymbol
End Function
Hope this will help. Best regards
I am currently working on a sheet where I would like to know how many times does a cell change in value. I have the following code:
Function Bidcalcul(Bid As Variant, Intervmin As Variant, Intervmax As Variant, Ticksize As Variant,
Percentage As Variant) As Variant
Dim rowcompteur As Integer
Dim valeurinitial As Variant
valeurinitial = ActiveCell.Value
rowcompteur = ActiveCell.Row
If IsError(Bid) Then
Bidcalcul = WorksheetFunction.Floor(Bid * Percentage, Ticksize)
End If
If Intervmin <= (Bid - valeurinitial) And (Bid - valeurinitial) <= Intervmax Then
Bidcalcul = valeurinitial
Else
Bidcalcul = WorksheetFunction.Floor(Bid * Percentage, Ticksize)
**Call Compteur(rowcompteur, 23)**
End If
End Function
Private Sub Compteur(rowcompteur As Integer, column As Integer)
Cells(rowcompteur, column).Value = Cells(rowcompteur, column).Value + 1
End Sub
But when calling the function Compteur it doesn't seems to work.
Do you have any idea on how I could do it? (I've already tried with a simple formula on excel but since I retrieve my values from Bloomberg it doesn't work)
Thanks!
Since UDF (user defined functions) do not allow changing other cell's values your attempt does not work.
As a workaround you can use a global/public variable Public Compteur As Long to count in your UDF Bidcalcul. And another volatile function Public Function GetCompteur() As Long to output that variable.
Option Explicit
Public Compteur(1 To 1048576) As Long
Public Function Bidcalcul(ByVal Bid As Variant, ByVal IntervMin As Variant, ByVal IntervMax As Variant, ByVal Ticksize As Variant, ByVal Percentage As Variant) As Variant
Dim ValeurInitial As Variant
ValeurInitial = Application.ThisCell.Value
Dim RowCompteur As Long ' row counting has to be long there are more rows than integer can handle!
RowCompteur = Application.ThisCell.Row ' don't use ActiveCell!
If IsError(Bid) Then
Bidcalcul = Application.WorksheetFunction.Floor(Bid * Percentage, Ticksize)
End If
If IntervMin <= (Bid - ValeurInitial) And (Bid - ValeurInitial) <= IntervMax Then
Bidcalcul = ValeurInitial
Else
Bidcalcul = Application.WorksheetFunction.Floor(Bid * Percentage, Ticksize)
Compteur(RowCompteur) = Compteur(RowCompteur) + 1
End If
End Function
Public Function GetCompteur() As Long
Application.Volatile
GetCompteur = Compteur(Application.ThisCell.Row)
End Function
Note that you should not use ActiveCell in a UDF because the active cell is not the cell the formula is written in. This is Application.ThisCell but any other cell on the worksheet could be the ActiveCell, so you might get wrong results!
Also your row counting variables need to be of type Long because Excel has more rows than Integer can handle. Actually I recommend always to use Long instead of Integer as there is no benefit in using Integer in VBA.
But your entire approach has one big issue
With ValeurInitial = Application.ThisCell.Value you reference to the same cell your formula is written in. So this genereates a circular reference and that is where you run into a problem. Because now if the formula calculates it's result changes and this changes thi initial value which would the formula need to calculate again and again.
Yes, this did not happen when you used ActiveCell.Value but that definitely gave you a wrong result. So you might need to re-think your approach or how you deal with the circular reference.
My recommendation to get out of this issue
Don't use UDFs for calculating this. Use a command button to launch a calculation procedure Sub and read your raw data into an array (for fast calculation) let your procedure do all the work and output the result in an output array that you can easily write into your sheet then.
This way you don't have to deal with circular references and you can easily count your iteration steps of your calulation. It gives you much more control over the entire process.
Your code looks good and it works on my excel. You could also try to write these Debug.Print instructions to see the values.
Debug.Print "Before: " & Cells(rowcompteur, column).Value
Cells(rowcompteur, column).Value = Cells(rowcompteur, column).Value + 1
Debug.Print "After: " & Cells(rowcompteur, column).Value
it works!
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.
Context:
I have several lists in my sheet (1 column wide, 1-10 rows long). When I right click a cell in these lists, I can do several options, that all work well. I have given a name to the cell at the top of each of these lists (ex. Cell A1 has been given the name cell_1, B10 is names cell_2, etc).
I would like to know if the cell I am right clicking on is the one at the top of the list; is it named "cell_(number)"? If it is not, it checks the cell on top of that one. Does it have a name that starts with "cell_"? If not, check the one on top, etc. Until I can figure out the user clicked on an element of WHICH list.
TL;DR The actual question
I can use ActiveCell.Address, which gives me something like "A1" whether or not I have assigned a name to that cell. ActiveCell.Name gives "Sheet1!A1", so it's not much better. Any idea how to get it to return the name I have assigned instead?
Create a UDF to test the application names, it's less efficient but contains error handling within the function itself:
Sub SO()
'// Example how to call function
Debug.Print GetCellName(Range("A1"))
End Sub
Function GetCellName(myCell As Excel.Range) As Variant
Dim nameCheck As Variant
For Each nameCheck In Application.Names
If Replace(Replace(Replace(nameCheck, "=", ""), "'", ""), "!", "") = _
CStr(myCell.Parent.Name & myCell.Address) Then
GetCellName = CStr(nameCheck.Name)
Exit Function
End If
Next
GetCellName = CVErr(Excel.xlErrName)
End Function
Note you can also use this function in a worksheet cell like so:
=GetCellName(A1)
Perhaps this would work. This function returns the names assigned to a cell (or bigger range for that matter). If there's more than one name, it returns it as an array for array formula...or the user can supply an index to return only the desired name position
Public Function CellIsInRangeNames(sheetname As String, checkRange As Range, Optional itemNumber As Variant) As Variant
Dim oNM As Name
Dim oSht As Worksheet
Dim isect As Range
Dim namesCollection() As Variant
Set oSht = Worksheets(sheetname)
Dim i As Integer
i = -1
For Each oNM In oSht.Names
Set isect = Application.Intersect(Range(oNM.Name), checkRange)
If Not isect Is Nothing Then
i = i + 1
ReDim Preserve namesCollection(0 To i)
namesCollection(i) = CStr(oNM.Name)
End If
Next oNM
If i = -1 Then
'didn't find any
CellIsInRangeNames = xlErrName
ElseIf Not IsMissing(itemNumber) Then
'user wanted this instance only
If (itemNumber - 1 > UBound(namesCollection)) Or (itemNumber - 1 < LBound(namesCollection)) Then
CellIsInRangeNames = xlErrValue
Else
CellIsInRangeNames = namesCollection(itemNumber - 1)
End If
Else 'here's the list as an array
CellIsInRangeNames = namesCollection
End If
End Function
I have an excel formula:
=SplitKey(GetSysCd(INDEX([ReportValue],MATCH("mtr_make_model",[FieldName],0)),INDEX([ListName],MATCH("mtr_make_model",[FieldName],0))), 0)
which is running a few subroutines in VBA, but mainly matching values and inserting those values into a cell. When it finds a value for "mtr_make_model" it runs and matches the values inside a sys codes table. The issue I am having is that it is calculating once and then it removes the formula and now has solely the value... In the event that I go to the mtr_make_model field and change the value, the formula does not recalculate. Has anyone heard of this happening? Is this due to something in the VBA code? How do I make that formula stay put and if certain values change, the formula recalculates?
Thanks in advance.
Here are the two functions:
Public Function GetSysCd(ByVal name As String, sysCdTableName As String) As String
Dim r As Integer
Dim sysCdTable As Range
Dim nameList As Variant
Dim sysCd As String
On Error GoTo GetSysCd_Error
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
nameList = WorksheetFunction.Index(sysCdTable, 0, 2)
r = WorksheetFunction.Match(name, nameList, 0)
sysCd = WorksheetFunction.Index(sysCdTable, r, 1)
GetOutOfHere:
On Error GoTo 0
GetSysCd = sysCd
Exit Function
GetSysCd_Error:
sysCd = ""
GoTo GetOutOfHere
End Function
Public Function SplitKey(s As String, v As Integer)
Dim aString As Variant
Dim r As Integer
If Len(s) > 2 Then
aString = Split(s, "_")
If v = 0 Or v = 1 Then
SplitKey = aString(v)
Else
SplitKey = aString(0)
End If
Else
SplitKey = ""
End If
End Function
I don't think the functions are relevant at this point, but rather just a matter of the function not recalculating when a variable in the formula changes...
The problem could be that Excel only recalculates functions when one of their arguments changes, and your GetSysCd function is referring to a range that is not in its argument list
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
where sysCdTableName is just a string rather than a reference.
You can make the functions recalculate in real time by adding Application.Volatile True to the top of each function.