Custom function in Excel for cell hyperlink creation - vba

For example, if i have a complex functions like this one: =IFERROR(CELL("address",INDEX(D:D,MATCH(A2;D3:D$750;0)+ROW(A2);1;1));"") i want to convert it into hyperlink which points to cell returned by that formula. And yeah, i know that it could be done by wrapping it with =HYPERLINK("[file_name.xlsm]" & %formula%) BUT, first of all, it looks very ugly and uncomfortable to read in the big sparse data, and second - it is very inconvenient to copy such data-blocks between Excel instances (and then replace filename).
So i wonder - is it possible to convert such output into cell-link for current file and sheet with readable text like "D156" in place of "[long_file_name.xlsm]$D$156"?
P.S. obviously i can make a custom function which returns current filename and put same formula into second argument of HYPERLINK func, but obviously it will be counter-effective, especially when i have huge chunks of data. So i want to wrap all this into 1 function...
P.P.S. Don't sure if this will be appropriate (it's kinda offtopic for this question), but it will be fine if the same functionality could be done with macro instead of custom function. So the macro should find all the duplicates inside of currently selected range and make hyperlinks (in cell with same offset on different sheet with offset +1, the next sheet after currently active) to next occurrence of every duplicate. The rest i can manage by myself.
UPD1:
Getting filename is not a problem, as i said in P.S., i can write a function like this:
Function GetCurFilename()
GetCurFilename= "[" & ActiveWorkbook.Name & "]"
End Function
but i still need somehow to send part of the first HYPERLINK's argument as second one!
UPD2:
I guess it should be done like this:
Function MakeLinkArgs(cAdrs) As SomeType_interpreted_as_2_arguments
If (cAdrs = "") Then
MakeLinkArgs(1) = ""
MakeLinkArgs(2) = ""
Else
MakeLinkArgs(1) = "[" & ActiveWorkbook.Name & "]" & cAdrs
MakeLinkArgs(2) = Replace(cAdrs, "$", "")
End If
End Function
So this func could be used like this: =HYPERLINK( MakeLinkArgs( IFERROR(CELL("address",INDEX(D:D,MATCH(A2;D3:D$750;0)+ROW(A2);1;1));"") ) )
But still no luck finding type which could be interpreted as 2 arguments (don't sure if such exists at all)...

Not totally sure what you are asking, but I think that =CELL("filename") will help you. This returns both the filename and the sheet. Just extract the sheet name and file name from what this returns. Also portable for cut/paste to other workbooks/worksheets.

Well, i didn't found a way to send multiple arguments with single result from other function, but i found a side way to do what i want. It looks like a dirty hack to me, but i don't sure, maybe it's the way to do things in Excel VBA...
So first it's needed to add a public variable at the top of the Module:
Public sBuff As String
Next, these 2 functions should be added after public var declaration:
Function MakeCLink(cAdrs)
If (cAdrs = "") Then
MakeCLink = ""
sBuff = ""
Else
MakeCLink = "[" & ActiveWorkbook.Name & "]" & cAdrs
sBuff = Replace(cAdrs, "$", "")
End If
End Function
Function sBuf() As String
sBuf = "" & sBuff
End Function
And after compilation it could be used in formulas as such: =HYPERLINK( MakeCLink(IFERROR(CELL("address",INDEX(D:D,MATCH(A2,D3:D$750,0)+ROW(A2),1,1)),"")), sBuf() )
Yes, it's ugly, but works as magic for me =)

Related

How do I change the text of multiple bookmarks by stepping through an array?

Sub initialize()
For boxNum = 1 To 10
vaultValuesForm.Controls("h" & boxNum).Value = ""
vaultValuesForm.Controls("d" & boxNum).Value = ""
Next boxNum
vaultValuesForm.Show
End Sub
Sub button_Populate_Click()
Dim array_h(9) As String, array_d(9) As String
For boxNum = 0 To 9
array_h(boxNum) = vaultValuesForm.Controls("h" & (boxNum + 1)).Value
array_d(boxNum) = vaultValuesForm.Controls("d" & (boxNum + 1)).Value
Next boxNum
Call populateTable(array_h(), array_d())
End Sub
Sub populateTable(array_0() As String, array_1() As String)
For x = 1 To 4
ThisDocument.Bookmarks("bd" & x).Range.Text = array_0(0)
Next x
End Sub
I have tested the functionality of this code at various points, and it works flawlessly right up until this line:
ThisDocument.Bookmarks("bd" & x).Range.Text = array_0(0)
Specifically, until it reaches = array_0(0). In its current state, reaching this point in the Sub results in "Run-time error '5941': The requested member of the collection does not exist." Same deal when I originally tried using = array_0(x) (which is ultimately what I'm trying to accomplish). However, if replaced with something direct such as = "AA", it works. How do I phrase this bit properly to set the bookmark values to those within the array?
Note: In case you're wondering, the arrays are being referenced and passed properly; I tested this by changing the loop to comments and using MsgBox() with various array elements.
The answer from comments. The issue I wasn't aware of was that the bookmarks were being deleted after running the module, so it wouldn't work again unless the bookmarks were created again.
Are you sure the bookmarks bd1...bd4 are still there in the document? Because a bookmark's range.text deletes the bookmark, so if you want to be able to repeat the bookmark text assignments you have to recreate the bookmarks after assigning the texts. FWIW I ran your code and it was fine when bd1..bd2 etc. existed but threw 5941 the next time. (This is quite a common problem!) – slightly snarky Sep 3 at 8:37
So, for the official answer to my question, the way I had done it initially is how; it just couldn't be repeated.

Parse enum values by name in VBA

I want to write some VBA code that should parse MSForms-constant names (given as a string, like "fmTextAlignLeft") into their actual value. Since there is no native way to do so I was considering to put the name of the constant into a powershell code that will then be executed and return the result.
Private Sub ParseEnumByName(EnumConst As String)
Dim WScript As New WshShell
Dim PSCode As String
Dim Result
PSCode = "(some code)" & EnumConst & "(more code with exit $Value statement)"
Result = WScript.Run("Powershell -command """ & PSCode & """", 0, True)
ParseEnumByName = Result
End Sub
This should be feasible by iterating through all enums in the MSForms library and get the values out of them with something like
[System.Enum]::GetNames( [System.Windows.Forms.FormBorderStyle] ) or maybe something like explained here: How to convert a string to a enum?
The problem is that the System.Windows.Forms library contains totally different enums and typenames than the MSForms library available in VBA.
I tried to Add-Type -Path "C:\Windows\SysWOW64\FM20.DLL" where the MSForms library is stored but it returns an error saying the file or assembly or some related file could not be found.
How may I get a reference to MSForms in Powershell?
Edit: I have actually found a demi-native way in VBA (Excel VBA only) to solve this issue without passing values to external script hosts. Please see below.
Here's the function I figured out. So far it seems to work with all pre-defined enums and constants and also self defined enums in Excel. The function must be placed in a module!
Static Function ParseValue(StringValue) As Variant
Dim ParseValueBuffer As Variant
If IsEmpty(ParseValueBuffer) Then
ParseValueBuffer = 1
Application.Run ("'ParseValue " & StringValue & "'")
ParseValue = ParseValueBuffer
ParseValueBuffer = Empty
Else
ParseValueBuffer = StringValue
End If
End Function
Sub TestMe()
MsgBox "First line" & ParseValue("vbcrlf") & "Second line"
MsgBox ParseValue("fmTextAlignCenter") 'Should return "2" (if MSForms is referenced)
MsgBox ParseValue("rgbblue") 'Should return 16711680
End Sub

VBA Run String as Code

I want to read strings of code from a database. A string should include pieces of text and one/two variables which are set later in the project. The result should look like this:
"Greetings, Tom."
Usually I would do something like this:
Sub Show()
Dim strName as String
strName = "Tom"
Debug.Print "Greetings, " & strName & "."
...
But in this case the part
"Greetings, " & strName & "."
is on my database. During runtime I set the value of strName to multiple names (eg in one case to "Tom" and in another case to "Marco")
How is it possible to let VBA know that strName is a variable and not a string to be shown? In the end my call should look like this:
strDbText = <String from Database>
strName = "Tom"
Debug.Print strDbText '--> Greetings, Tom.
Can anyone help me out one more time?
Thanks a lot!
edit:
This code will not be an executable. So there is no need to go for the VBIDE-Objects to create a new module and execute inserted code. It is a string which will be written in a Word Document.
Solution:
String Substitution with the Replace() function
I would assign strName first and replace it with the string in the Database like this...
strName = "Tom"
strDbText = <String from Database>
strDbText = Replace(strDbText, "strName", strName) 'This distinguishes the string from the variable
Debug.Print strDbText '--> Greetings, Tom.
However, I would suggest using another variable in your database in place of strName because it may become confusing to look at.
Absolutely agreeing #ShanayL answer and strongly recommending its usage, I couldn't avoid pointing an unsafe alternative to solve your question, using Eval. Eval is potentially dangerous, creating a code injection vulnerability in your application!!!
strDbText = """Greetings, "" & strName"
strName = "Tom"
Debug.Print Eval(strDbText) 'Greetings, Tom
Notice: Eval is potentially dangerous and its usage is strongly disencouraged!

VBA Function Not Storing String [duplicate]

This question already exists:
VBA Function not storing variable
Closed 5 years ago.
I am reposting this question, because I haven't gotten an answer and I still can't figure out what I'm doing wrong. My latest efforts to resolve the problem on my own are detailed below the code.
Original post: VBA Function not storing variable
I have included the code of my function. I mostly scrapped this together from things I found online, because I am very much an amateur coder. I am trying to take the trendline of a graph and use it for a mathematical calculation. When I step through this code, it works great. However, when I call the function from another sub, it gives me an error. Error 9: Subscript out of range. When I debug, it shows me the line a = spl(0). The real problem is that the variable "s" remains empty. Why?
Function TrendLineLog() As Double
Dim ch As Chart
Dim t As Trendline
Dim s As String
Dim Value As Double
' Get the trend line object
Set ch = ActiveSheet.ChartObjects(1).Chart
Set t = ch.SeriesCollection(1).Trendlines(1)
' make sure equation is displayed
t.DisplayRSquared = False
t.DisplayEquation = True
' set number format to ensure accuracy
t.DataLabel.NumberFormat = "0.000000E+00"
' get the equation
s = t.DataLabel.Text '<--------- ACTUAL PROBLEM HERE
' massage the equation string into form that will evaluate
s = Replace(s, "y = ", "")
s = Replace(s, "ln", " *LOG")
s = Replace(s, " +", "")
s = Replace(s, " - ", " -")
spl = Split(s, " ")
a = spl(0) '<----------- DEBUG SAYS HERE
b = spl(1)
c = spl(2)
y = 0.5
..... Math stuff
End Function
I have tried adding the creation of the chart to the function to avoid an error with "Active Sheet". I also tried pasting this code into my sub instead of calling a separate function. Still nothing. When I debug and highlight the t.DataLabel.Text, it shows me the correct value, but for some reason s is not saving that value. In the Locals window, t has value, but s is blank (" ").
Yes, of course you will get an error on the line you pointed out. You are calling spl(0) as though it is its own function, though you did not define spl() as a sub (function) anywhere in this code. Or, alternatively (more likely) you are calling it as an array, which also throws up some flags.
Make sure you are defining spl in your code. You never do this. Add line:
Dim spl(1 to 3) As String
Then you should find that spl(1), spl(2), and spl(3) are what you desire.

How can i change in VBA the custom format of a cell to Generic "randomtext"?

If i change it manually it works.
If then i run this:
Sub cellformat()
ShowCellFormat = Range("A1").NumberFormat
MsgBox ShowCellFormat
Range("A2").NumberFormat = ShowCellFormat
End Sub
this works too, msgbox displays:
Generic "randomtext"
The only thing that i cant manage is to change the format like this:
Range("A1").NumberFormat = "Generic "randomtext""
Tried 100 different ways still not working...
Assuming you want your number format to be something like "XYZ"0000"DEF" to display the number 15 as XYZ0015DEF, then you can't write your code as
Range("A1").NumberFormat = ""XYZ"0000"DEF""
'or
'Range("A1").NumberFormat = "Generic "randomtext""
You instead need to write the code as:
Range("A1").NumberFormat = """XYZ""0000""DEF"""
'or
'Range("A1").NumberFormat = "Generic ""randomtext"""
because each double-quotation mark (i.e. ") used within a string literal needs to be escaped by writing it as "".