Turn String Into Hyperlink with Preset Name in Excel with VBA - vba

I have a userform where I will be wanting people to enter a link e.g. bbc.com & I want the program to automatically turn this string into a hyperlink (blue underlined) called "website".
Here's what I have so far.
PublicProperty Get Link() as string
Link=Me.Linkbvalue
.cells(blankrow,1).value=me.link
EDIT: Note, words generic to protect company. Actually the problem is not it starting with local server. In the following the file is in the folder but not the subfolder
Entry="\directory\folders_directory\folder\file"
when running code,
address becomes
\directory/folders_directory\folder\sub_folder"\directory\folders_directory\folder\file"

The following might help.
It will convert column A values to hyperlinks.
Sub GetHyperlink()
For Each xCell In Range("A:A")
If xCell.Value <> "" Then
ActiveSheet.Hyperlinks.Add Anchor:=xCell, Address:="http://www." & Replace(xCell.Formula, "www.", ""), TextToDisplay:="Website"
End If
Next xCell
End Sub

This is something that should work, if you read the the entry variable from the Form:
Sub TestMe()
Dim entry As String
entry = "bbc.com"
Dim httpPrefix As String
httpPrefix = "https://"
If Left(entry, Len(httpPrefix)) <> httpPrefix Then
entry = httpPrefix & entry
End If
With Worksheets(1)
.Hyperlinks.Add Anchor:=.Cells(1, 1), _
Address:=entry, _
TextToDisplay:="website"
End With
End Sub
Some business logic is needed to decide whether to write https:// or http:// or anything similar. You may consider using Trim() to remove possible empty cells from the left and the right.

Related

VBA Excel - run string variable as a line of code

In the aim to allow users from different countries to use my application, I would like to initialize a translation of each object in each existing userform (labels,commandbuttons,msgbox,frames, etc...) at the start of the application.
I'll write all the translation in my Languages sheet:
I've already made a first userform where the user types his login, password and selects his language.
After this step, the main userform called "Menu" will be launched.
I've already tried to type a piece of code (here below) to find the line of code, in a msgbox that I want to run (example : menu.commandbutton1.caption="Envoyer email")
Private Sub UserForm_Initialize()
' Definition of language selected during login
Set langue = Sheets("Languages").Cells.Find("chosen",
lookat:=xlWhole).Offset(-1, 0)
' Initialisation of the texts in the selected language
Dim cel As Range
Dim action As String
For Each cel In Sheets("Languages").Range("d3:d999")
If cel <> "" Then
action = cel & "=" & """" & cel.Offset(0, -2) & """"
MsgBox (action)
End If
Next cel
End Sub
I've already read some topics about this subject but those does not correspond exactly to what i would like to do.
If you have a solution, or a work around, it would be very helpful.
If you simply want different MsgBox, based on a coutry, this is probably the easiest way to achieve it. Imagine your file is like this:
Then something as easy as this would allow you to use different strings, based on the country:
Public Sub TestMe()
Dim country As String
Dim language As Long
country = "Bulgaria" 'or write "England" to see the difference
language = WorksheetFunction.Match(country, Range("A1:B1"), 0)
MsgBox (Cells(2, language))
MsgBox "The capital of " & country & " is " & (Cells(3, language))
End Sub
The idea of the whole trick is simply to pass the correct column, which is done through WorksheetFunction.Match.
Taken from an old CR post I have here, this solution pretty much mimicks .NET .resx resource files, and you can easily see how to extend it to other languages, and if I were to write it today I'd probably use Index+Match lookups instead of that rather inefficient loop - but anyway it works nicely:
Resources standard module
Option Explicit
Public Enum Culture
EN_US = 1033
EN_UK = 2057
EN_CA = 4105
FR_FR = 1036
FR_CA = 3084
End Enum
Private resourceSheet As Worksheet
Public Sub Initialize()
Dim languageCode As String
Select Case Application.LanguageSettings.LanguageID(msoLanguageIDUI)
Case Culture.EN_CA, Culture.EN_UK, Culture.EN_US:
languageCode = "EN"
Case Culture.FR_CA, Culture.FR_FR:
languageCode = "FR"
Case Else:
languageCode = "EN"
End Select
Set resourceSheet = Worksheets("Resources." & languageCode)
End Sub
Public Function GetResourceString(ByVal resourceName As String) As String
Dim resxTable As ListObject
If resourceSheet Is Nothing Then Initialize
Set resxTable = resourceSheet.ListObjects(1)
Dim i As Long
For i = 1 To resxTable.ListRows.Count
Dim lookup As String
lookup = resxTable.Range(i + 1, 1)
If lookup = resourceName Then
GetResourceString = resxTable.Range(i + 1, 2)
Exit Function
End If
Next
End Function
The idea is, similar to .NET .resx files, to have one worksheet per language, named e.g. Resources.EN and Resources.FR.
Each sheet contains a single ListObject / "table", and can (should) be hidden. The columns are basically Key and Value, so your data would look like this on sheet Resources.EN:
Key Value
menu.caption Menu
menu.commandbutton1.caption Send email
menu.commandbutton1.controltiptext Click to send the document
And the Resources.FR sheet would have a similar table, with identical keys and language-specific values.
I'd warmly recommend to use more descriptive names though; e.g. instead of menu.commandbutton1.caption, I'd call it SendMailButtonText, and instead of menu.commandbutton1.controltiptext, I'd call it SendMailButtonTooltip. And if your button is actually named CommandButton1, go ahead and name it SendMailButton - and thank yourself later.
Your code can then "localize" your UI like this:
SendMailButton.Caption = GetResourceString("SendMailButtonText")
The Resources.Initialize procedure takes care of knowing which resource sheet to use, based on Application.LanguageSettings.LanguageID(msoLanguageIDUI) - and falls back to EN, so if a user has an unsupported language, you're still showing something.

Reading a barcode in excel to see if there is a match

I am working with Excel 2016. I have a smidgen of experience with VBA for applications, and some experience with programming.
I'm trying to take input from a barcode scanner, compare it to a column in a spreadsheet, and if there's a match, put a few characters and a date stamp in some cells (Initials and date, each in separate columns).
This question has a very similar use-case, and includes a code sample. I have tried the code sample and can't get it to work. At first, there was a problem with the array. Eventually I figured out you could do "C2:C8" and that seemed to work, though that's not documented anywhere (Probably part of a basics course/class, but not findable). There was an error about sub or function defined for Match(), so I enabled the Solver Add-in in the security center. That didn't fix it, so I found this forum post that explained Match wasn't a VBA function.
Now, I get an error after clicking the button "Run time error 1004, unable to get Match property of the WorksheetFunction class", clicking debug takes me to the same line.
Here is the code I have wound up with:
Private Sub CommandButton1_Click()
code = InputBox("Please scan a barcode and hit enter if you need to")
matchedCell = Application.WorksheetFunction.Match(code, Range("C2:C8"), 0)
matchedCell.Offset(0, 2) = Now
End Sub
This is incredibly frustrating because I thought this was a simple thing and already solved. Instead of working to solve the problem and build software, it seems I'm fighting syntax and/or the environment. What am I doing wrong?
two possibilities:
use Match() function of Application object
and store its returned value in a Variant variable to be checked for any error (if value not found)
Private Sub CommandButton1_Click()
Dim code As Variant
Dim matchedCell As Variant
code = InputBox("Please scan a barcode and hit enter if you need to")
matchedCell = Application.Match(code, Range("C2:C8"), 0)
If Not IsError(matchedCell) Then Range("C2:C8").Cells(matchedCell, 1).Offset(0, 2).Value = Now
End Sub
use Find() function of Range object
Private Sub CommandButton1_Click()
Dim code As Variant
Dim matchedCell As Range
code = InputBox("Please scan a barcode and hit enter if you need to")
Set matchedCell = Range("C2:C8").Find(what:=code, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=True)
If Not matchedCell Is Nothing Then matchedCell.Offset(0, 2).Value = Now
End Sub
Use Application.Match , and continue running your code only if there is a successful Match.
Option Explicit
Private Sub CommandButton1_Click()
Dim MatchRow As Variant
Dim code As Variant
Dim matchedCell As Range
code = InputBox("Please scan a barcode and hit enter if you need to")
' verify that there is a successful match in the searched range
If Not IsError(Application.Match(code, Range("C2:C8"), 0)) Then
MatchRow = Application.Match(code, Range("C2:C8"), 0) '<-- get the row number
Set matchedCell = Range("C" & MatchRow + 1) '<-- set the range (add 1 row since you are starting from row 2)
matchedCell.Offset(0, 2).Value = Now
'option 2: without setting the range
Range("C" & MatchRow).Offset(1, 2).Value = Now
End If
End Sub

Filename in variable used for formulas and copying

I am trying to use a wildcard filename as a variable so I can use it to copy and do some formulas. And then I want to flatten all the formulas.
It looks like this:
This first part works (first thing opens wildcard file from a cell formula and second assigns only filename without path to variable Prod - hovering over variable prod gives exactly what it should)
Dim wbProd As Workbook
Windows("SB.xlsm").Activate
Set wbProd = Workbooks.Open(FileNAME:=Sheets("refs").Range("B48").Value)
Dim Prod As String
Windows("SB.xlsm").Activate
Prod = Worksheets("refs").Range("B49").Value
Windows("Weekly.xlsx").Activate
With Workbooks(" & Prod & ").Sheets("Report 1")
.Range("A2:BG10", .Range("A2:BG10").End(xlDown)).Copy Workbooks("WeeklyData X.xlsx").ActiveSheet.Range("A2")
End With
Windows("WeeklyData X.xlsx").Activate
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Report 1")
ws.UsedRange.Value = ws.UsedRange.Value
I am getting an error with this first part of copying: With Workbooks(" & Prod & ").Sheets("Report 1"). When I use this copying method without using filename in a variable it works and also when I use variable filename to do Vlookups it works. I dont know what would be the reason not to work here.
Also if you have better way to flatten all the formulas and preseve formats (coz of dates) it would be great.
Thanks,
A quick fix would be to create a Workbook variable (Dim myWB as Workbook),
Then do Set myWB = Workbooks(Prod). Then just do With myWB.Sheets("Sheet1").
The issue is that Excel needs quotes in the sheet name, and so your book is literally being understood as being titled & Prod &. So, to keep your current idea, you need to just add an additional quote to each quote: With Workbooks("" & Prod & "").Sheets("Report 1").
Personally I recommend setting up a workbook variable, but either works!
Edit:
#drLecter - Very welcome! You'll also run into the "double quotes" issue when trying to set up formulas that have quotes in them. IE The worksheet formula =Vlookup("myText",A1:D1,2,False) would, in VBA, become
Cells(1,1).Formula = "=Vlookup(""myText"",A1:D1,2,False)".
As you can see, if I didn't use double quotes, VBA would stop reading the formula at
Cells(1,1).Formula = "=Vlookup(
Use dir() !
Microsoft Documentation link - dir() function
-Returns a string representing the name of a file, directory, or folder that matches a specified pattern or file attribute, or the volume label of a drive.
just adapt something like this::
Sub LoopThroughFiles()
Dim MyObj As Object, MySource As Object, file As Variant
file = Dir("c:\testfolder\")
While (file <> "")
If InStr(file, "test") > 0 Then
MsgBox "found " & file
Exit Sub
End If
file = Dir
Wend
End Sub

Write a VLOOKUP as a string in a cell with dynamic path retrieved through GetoOpenfilename

I am trying to write a VLOOKUP in a cell as a string, with VBA. This means that I do not want the result to appear in the cell as a value, but I want the whole VLOOKUP expression instead (For this example : "VLOOKUP(C6,'[path_to_file.xlsm]OTD Table!$B:$F,4,0)"). The challenge is that the range argument of the VLOOKUP is a concatenation of a path (path_to_file.xlsm) that the user selects with a GetOpenFilename, and a string that specifies the tab in which the lookup table is located ("OTD Table!$B:$F,4,0").
The issue I am getting is very interesting :
When I print my expression in a Msgbox, the expression appears correctly. However, when I write it in a cell, the path mysteriously appears incorrectly.
Sub macro()
dim data_file_new as String
data_file_new = CStr(Application.GetOpenFilename(FileFilter:="Excel Workbooks (*.xls*),*.xls*", Title:="Select new data file")) ' The user selects the file
str_ = "=VLOOKUP(C6," & "'[" & data_file_new & "]OTD Table!$B:$F,4,0)" ' This will display the expression correctly
cells(1,10)="=VLOOKUP(C6," & "'[" & data_file_new & "]OTD Table!$B:$F,4,0)"' This will not display the same thing as in the messagebox above
end Sub
I hope one of you guys can make sens of this !
Because you're dropping a formula into a cell that you want to display as straight text, you have to be explicit with Excel and tag the text string to prevent interpreting it as a formula. The simplest way to do this is pre-pend the string with a single-quote "'".
Sub macro()
Dim data_file_new, str_ As String
str_ = "'=VLOOKUP(C6,'["
data_file_new = CStr(Application.GetOpenFilename(FileFilter:="Excel Workbooks (*.xls*),*.xls*", Title:="Select new data file")) ' The user selects the file
str_ = str_ & data_file_new & "]OTD Table!$B:$F,4,0)" ' This will display the expression correctly
ActiveSheet.Cells(1, 10).Value = str_
End Sub
Yeah either you'll need to set the string to add a single quote, or you'll need to change the numberformat of the cell to text (Cells(1,10).NumberFormat = "#")
Either of those should work.

Is it possible in Excel VBA to change the source code of Module in another Module

I have an Excel .xlam file that adds a button in the ribbon to do the following:
Scan the ActiveSheet for some pre-set parameters
Take my source text (a string value, hard coded directly in a VBA Module) and replace designated areas with the parameters retrieved from step 1
Generate a file containing the calculated text
I save the source text this way because it can be password protected and I don't need to drag another file around everywhere that the .xlam file goes. The source text is saved in a separate module called "Source" that looks something like this (Thanks VBA for not having Heredocs):
'Source Module
Public Function GetSource() As String
Dim s As String
s = ""
s = s & "This is the first line of my source text" & vbCrLf
s = s & "This is a parameter {par1}" & vbCrLf
s = s & "This is another line" & vbCrLf
GetSource = s
End Function
The function works fine. My problem is if I want to update the source text, I now have to manually do that in the .xlam file. What I would like to do is build something like a Sub ImportSource() in another module that will parse some file, rebuild the "Source" Module programatically, then replace that Module with my calculated source code. What I don't know is if/how to replace the source code of a module with some value in a string variable.
It's like metaprogramming at its very worst and philosophically I'm against doing this down to my very core. Practically, however, I would like to know if and how to do it.
I realize now that what you really want to do is store some values in your document in a way that is accessible to your VBA, but that is not readable to a user of the spreadsheet. Following Charles Williams's suggestion to store the value in a named range in a worksheet, and addressing your concern that you don't want the user to have access to the values, you would have to encrypt the string...
The "proper way" to do this is described in this article - but it's quite a bit of work.
A much shorter routine is found here. It just uses simple XOR encryption with a hard coded key - but it should be enough for "most purposes". The key would be "hidden" in your macro, and therefore not accessible to prying eyes (well, not easily).
Now you can use this function, let's call it encrypt(string), to convert your string to a value in the spreadsheet:
range("mySecretCell").value = encrypt("The lazy dog jumped over the fox")
and when you need to use it, you use
Public Function GetSource()
GetSource = decrypt(Range("mySecretCell").value)
End Function
If you use the XOR version (second link), encrypt and decrypt would be the same function...
Does that meet your needs better?
As #brettdj already pointed out with his link to cpearson.com/excel/vbe.aspx , you can programmatically change to code of a VBA module using the VBA Extensibility library! To use it, select the library in the VBA editor Tools->References. Note that you need to also change the options in your Trust center and select: Excel Options->Trust Center->Trust Center Settings->Macro Settings->Trust access to the VBA project object model
Then something like the following code should do the job:
Private mCodeMod As VBIDE.CodeModule
Sub UpdateModule()
Const cStrModuleName As String = "Source"
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = Workbooks("___YourWorkbook__").VBProject
'Delete the module
VBProj.VBComponents.Remove VBProj.VBComponents(cStrModuleName)
'Add module
Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = cStrModuleName
Set mCodeMod = VBComp.CodeModule
'Add procedure header and start
InsertLine "Public Function GetSource() As String"
InsertLine "Dim s As String", 1
InsertLine ""
'Add text
InsertText ThisWorkbook.Worksheets("Sourcetext") _
.Range("___YourRange___")
'Finalize procedure
InsertLine "GetSource = s", 1
InsertLine "End Function"
End Sub
Private Sub InsertLine(strLine As String, _
Optional IndentationLevel As Integer = 0)
mCodeMod.InsertLines _
mCodeMod.CountOfLines + 1, _
Space(IndentationLevel * 4) & strLine
End Sub
Private Sub InsertText(rngSource As Range)
Dim rng As Range
Dim strCell As String, strText As String
Dim i As Integer
Const cLineLength = 60
For Each rng In rngSource.Cells
strCell = rng.Value
For i = 0 To Len(strCell) \ cLineLength
strText = Mid(strCell, i * cLineLength, cLineLength)
strText = Replace(strText, """", """""")
InsertLine "s = s & """ & strText & """", 1
Next i
Next rng
End Sub
You can "export" and "import" .bas files programmatically. To do what you are asking, that would have to be the approach. I don't believe it's possible to modify the code in memory. See this article